Skip to content
Developers
Esc
↑↓navigate↵open⌘Jpreview
On this page

Errors

The Qaf API's JSON error format, every status and code it returns, which errors to retry, and how request IDs help support find a request.

An error response has a 4xx or 5xx status and a JSON body with the same shape on every endpoint:

{
  "code": "NOT_FOUND",
  "status": 404,
  "message": "No passage has ID d9335f69-e54b-59ae-a78c-50db734b8d51."
}
Field Description
code A stable, machine-readable code. Branch on this, not on message.
status The HTTP status, repeated.
message A sentence for people. Its wording can change.
data Details for some codes, described below. Left out when there are none.

Status codes

Status code When Retry
400 BAD_REQUEST The body isn’t valid JSON, or the request can’t be run as sent, such as a filter with an unknown author ID. No. Fix the request.
401 UNAUTHORIZED The API key is missing, invalid or revoked. The WWW-Authenticate header is Bearer realm="api.qaf.ai", with error="invalid_token" added when the request sent a key. No.
403 FORBIDDEN The key’s account can’t use the API. No.
404 NOT_FOUND No passage has the ID, or no endpoint matches the method and path. No.
405 METHOD_NOT_SUPPORTED The path exists but takes another method. The Allow header lists the methods it takes. No.
413 PAYLOAD_TOO_LARGE The request body is larger than 64 KiB. No.
422 INPUT_VALIDATION_FAILED A field is missing, has the wrong type or breaks a limit, or the search body has a field it doesn’t define. No. Fix the request.
429 RATE_LIMITED The account went over a rate limit, or your address sent too many keys that don’t exist (invalid_keys). Only when data.reason is minute, after Retry-After seconds.
500 INTERNAL_SERVER_ERROR Something went wrong on our side. Yes, with backoff.
503 SERVICE_UNAVAILABLE Qaf couldn’t finish the request within 30 seconds, or a service it relies on is down. Yes, with backoff.

Handle codes you don’t know by their status: new codes can appear in v1.

Validation errors

A 422 lists the problems in data. fieldErrors is keyed by the field’s name, and formErrors holds problems that belong to no single field:

{
  "code": "INPUT_VALIDATION_FAILED",
  "status": 422,
  "message": "✖ Too big: expected number to be <=15\n  → at limit",
  "data": {
    "formErrors": [],
    "fieldErrors": {
      "limit": ["Too big: expected number to be <=15"]
    }
  }
}

A field the search body doesn’t define appears in fieldErrors under the top-level field that holds it: topK under topK, and an unknown filter key under filters. message gives the full path, so you can tell which field to drop:

{
  "code": "INPUT_VALIDATION_FAILED",
  "status": 422,
  "message": "✖ Unrecognized key: \"categoryIds\"\n  → at filters.categoryIds",
  "data": {
    "formErrors": [],
    "fieldErrors": {
      "filters": ["Unrecognized key: \"categoryIds\""]
    }
  }
}

When the unknown key is the only one in filters, filters also lists Specify at least one filter, or omit filters for an unrestricted query.

Requests that can’t be run

A 400 from POST /v1/search carries data.reason:

reason Means
unknown_author_id An ID in filters.authorIds isn’t in the catalog. Look the author up with GET /v1/catalog/lookup.
unknown_book_id An ID in filters.bookIds isn’t in the catalog.
no_matching_books No book can match the filters together, such as a genre and a death-year range none of its authors fall in.

New reasons can appear in v1. When reason is missing or unknown, show message.

Rate limit errors

A 429 has a Retry-After header and data:

{
  "code": "RATE_LIMITED",
  "status": 429,
  "message": "Too many requests. Try again later.",
  "data": { "retryAfter": 23, "reason": "minute" }
}

retryAfter is the same number of seconds as the header. reason says which limit was reached. Only a minute refusal is worth waiting out: a day refusal can last most of a day, and invalid_keys means the key you sent doesn’t exist or was revoked. See Back off and retry.

Request IDs

Every API response, including errors, has an X-Request-Id header. The only exception is the answer to a browser’s CORS preflight (OPTIONS) request. Log the ID with failed requests, and include it when you email us at contact@qaf.ai, so we can find the request.