Errors & status codes
Every status code, the stable error envelope, and why a 200 can still be an incomplete live search.
Base URL https://api.offendersearch.appThe error shape
Criminal endpoints return errors as a single, consistent nested object across every endpoint: a stable error.code you switch on and a human-readable error.message you can surface. One handler covers the whole product. Branch on code, render message — code is a stable machine token; message may be reworded.
{
"error": {
"code": "guard_unbounded_query",
"message": "a lastName predicate is required — an unbounded query returns a lottery, not an answer"
}
}A 200 can still describe an incomplete live search: read status and counts, never records alone.
Status codes
| Status | error.code | When you’ll see it |
|---|---|---|
| 200 | — | A cached search, or a synchronous live search, completed. A cached search is always complete; a live search may report unfinished sources in counts. |
| 202 | — | A live search fanned out to more than two sources; poll GET /v1/criminal/searches/{searchId} for the result. |
| 400 | no_report_input | A report request with neither a searchId nor an inline query. |
| 401 | unauthenticated | The X-API-Key header is missing or malformed. |
| 404 | not_found | No search or record carries the id you asked for, or it belongs to another account. |
| 413 | batch_too_large | A batch of more than 1000 rows. |
| 422 | guard_unbounded_query | query.lastName is missing — an unbounded query is declined rather than answered with a lottery. |
| 422 | prefix_too_short | An explicit name-prefix search below the three-character floor. |
| 422 | no_live_sources | A live request (carrying no website inputs) that resolved to no sources. |
| 422 | no_website | A resolve call with neither website nor websites. |
| 422 | bad_batch / empty_batch | An unparseable batch body or a CSV with no header row, or zero queries in the batch. |
There is no request rate limit today. No endpoint returns 429, and no Retry-After header is emitted. Apply your own client-side concurrency control — a cached search over a common surname can return up to 200 records per page and many pages.
Two errors, verbatim
Unauthenticated
{
"error": {
"code": "unauthenticated",
"message": "the X-API-Key header is missing or malformed"
}
}Declined query
{
"error": {
"code": "guard_unbounded_query",
"message": "a lastName predicate is required — an unbounded query returns a lottery, not an answer"
}
}422 is a deliberate contract, not a failure. A query with nothing to narrow on cannot return the same full result twice, so it is declined with a sentence you can show a user rather than answered with a subset that would read as the whole answer.