Developer docs · v1.0.0

Errors, rate limits & retries

One error shape, a Retry-After you can trust, and a short list of what to retry.

The error body

Two shapes, by product. Sex Offender, Monitoring, Map, FCRA and every `401`/`402`: JSON with a detail. For a field validation failure it is a list, one entry per field, each with loc (the path to the field), msg and type; for a refused request an object with a code and a sentence; on a 429 an object with a code and retryAfterSeconds. Criminal Records refusals (`4xx`/`5xx` from the service): JSON with an error object carrying code and message. Branch on the code, never on the sentence.

// Sex Offender refusal
{ "detail": { "code": "query_too_broad",
              "message": "Add a first name, a date of birth or a state." } }

// Criminal Records refusal
{ "error": { "code": "query_too_broad",
             "message": "Add a first name, a date of birth or a state." } }

Status codes

StatusMeaningRetry?
200The answer. Read status (complete / partial) before records.—
202Accepted: an async job with an id to poll (POST /v1/searches, live.evidence, a criminal search with more than two live sources). A Sex Offender live search that goes async answers 200 with status: "pending" and a poll path instead.—
400 / 422The request was refused: a missing required field, an invalid value, a query too broad to answer honestly. detail (criminal: error.code) says which.No — fix the request.
401Authentication failed.No — fix the key or the host.
403Not entitled (Map API, FCRA partner, consumer account on a business route).No — enable it.
404Unknown id or path.No.
429Rate limited or at capacity; nothing was searched or billed. Retry-After carries the seconds to wait (1–120); on the Sex Offender API detail.retryAfterSeconds repeats it. A criminal 429 comes from the gateway after it has queued the request for up to 45 s and may carry no JSON body — read the header.Yes — after Retry-After.
5xxThe platform failed or a dependency timed out. Nothing was searched; nothing was billed. (504 also answers a Sex Offender search that hit its deadlineMs with onDeadline: "error", or that found nothing before some registries finished: incomplete_no_results.)Yes — with backoff, for requests that are safe to repeat (below).

What to retry

  • Safe to retry: every GET; a synchronous search without a live block; 429 after Retry-After.
  • Retry with care: a synchronous search *with* a live block (a repeat re-checks the sources again and bills again) — read the error first.
  • Never retry blindly: POST /v1/searches (async). Every accepted request is a new, billed job and there is no idempotency header. Persist the searchId from the 202 and recover by polling it. See Async jobs & webhooks.
import time, requests

def call(method, url, **kw):
    for attempt in range(4):
        r = requests.request(method, url, timeout=60, **kw)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        if 500 <= r.status_code < 600 and method == "GET":
            time.sleep(2 ** attempt)
            continue
        return r
    return r

Rate limits

Limits are per account and are enforced as 429 with Retry-After; there is no fixed public number, because the platform queues briefly before refusing and the headroom depends on your plan. A 429 is never a partial answer: nothing was searched and nothing was billed. Product pages note where a product queues or caps differently (Sex Offender errors, Criminal errors, Map limits).