Reference

Error Codes

All error responses follow a consistent JSON structure. The error.code field contains a machine-readable string identifier that you should use in your error handling logic. Never rely on the message field, which may change.

Error response format

JSON
{
  "success": false,
  "error": {
    "code": "FACE_NOT_FOUND",
    "message": "No face found with the given ID in this collection."
  },
  "requestId": "0c772a2c-af02-413e-9ef0-c7da186d724d",
  "timestamp": "2026-09-20T11:22:33Z"
}

HTTP status codes

StatusMeaning
200 OKRequest succeeded.
201 CreatedResource created successfully.
400 Bad RequestMissing or invalid parameters.
401 UnauthorizedMissing or invalid authentication credentials.
402 Payment RequiredA plan limit was reached — collections, faces, or API keys.
403 ForbiddenValid credentials but insufficient permissions or IP blocked.
404 Not FoundRequested resource does not exist.
409 ConflictResource already exists (e.g. duplicate external_id).
422 Unprocessable EntityValidation passed but business logic rejected the request.
429 Too Many RequestsYour organization's per-minute limit was reached. Wait for Retry-After.
500 Internal Server ErrorUnexpected server error. Contact support with the requestId.
503 Service UnavailableThe recognition engine is busy for a moment. Wait for Retry-After and send the request again.

API key errors

CodeHTTPDescription
UNAUTHORIZED401No X-API-Key header was sent.
INVALID_API_KEY401The key does not exist, or it was revoked.
API_KEY_EXPIRED401The key passed the expiry date set when it was issued.
API_KEY_INACTIVE401The key was disabled in the console without being deleted.
INSUFFICIENT_SCOPE403The key lacks face:read or face:write for this call.
COLLECTION_NOT_ALLOWED403The key is restricted to other collections.
IP_NOT_ALLOWED403The calling address is not in the key's whitelist.

Face & recognition errors

CodeHTTPDescription
FACE_NOT_FOUND404No face with the given face_id or external_id in this collection.
NO_FACE_DETECTED422No face was detected in the submitted image.
MULTIPLE_FACES422More than one face detected; submit a single-face image.
FACE_QUALITY_TOO_LOW422Quality score below threshold (blur, occlusion, extreme pose).
SPOOF_DETECTED422The collection requires liveness at enrolment and the image did not pass. Capture the face live.
EXTERNAL_ID_EXISTS409An active face with this external_id already exists in the collection.

Collection & API key errors

CodeHTTPDescription
COLLECTION_NOT_FOUND404Collection does not exist or belongs to a different org.
COLLECTION_LIMIT_EXCEEDED402Plan collection limit reached. Upgrade to create more.
FACE_LIMIT_EXCEEDED402Collection face limit reached for your current plan.
API_KEY_LIMIT_EXCEEDED402API key limit per member reached for your current plan.
RATE_LIMIT_EXCEEDED429Your organization's per-minute limit (or the key's own lower limit) was reached. See the Retry-After header.
SERVICE_BUSY503The recognition engine is saturated for a moment; not your limit. See the Retry-After header.
INVALID_RATE_LIMIT400A key's customRateLimitRpm must be from 1 up to your plan's rate.

Validation errors

A malformed request fails on the first problem found and names it in error.code. There is no aggregated details array — fix one, resend, see the next.

JSON
{
  "success": false,
  "error": {
    "code": "IMAGE_REQUIRED",
    "message": "image file is required"
  },
  "requestId": "49d93b21-76e1-47ef-9e01-be5f02df47ec",
  "timestamp": "2026-09-20T11:12:57Z"
}

Handling rate limits

A 429 (your limit) and a 503 SERVICE_BUSY (the engine is busy) both carry a Retry-After header with the number of seconds to wait before retrying:

async function identifyWithRetry(formData, apiKey, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch(
      'https://api.livexface.com/api/v1/collections/COLLECTION_ID/identify',
      { method: 'POST', headers: { 'X-API-Key': apiKey }, body: formData }
    )
    if (res.status === 429 || res.status === 503) {
      const wait = parseInt(res.headers.get('Retry-After') ?? '5', 10)
      await new Promise(r => setTimeout(r, wait * 1000))
      continue
    }
    const data = await res.json()
    if (!data.success) throw new Error(data.error.code)
    return data
  }
  throw new Error('Max retries exceeded')
}