Regular & Live modes, and source coverage
Two modes — Regular (continuously updated) and Live — per-record lastCheckedAt, and the live coverage catalog.
Base URL https://api.offendersearch.appTwo modes: Regular, and Live verification
There is one search endpoint, and which mode you get is decided by the request body. Omit the live block and you get the Regular answer — the continuously-updated data we hold, close to live, returned in one round trip. Send a live block and the API re-verifies the named jurisdiction(s) in real time. Both modes run every result through the same match logic and return the same response envelope, so a Regular record and a Live record agree about identity and rank together — the choice is about how the answer is obtained, not about a different product.
| Regular (default) | Live verification | |
|---|---|---|
| How you ask | Omit live | Send a live block |
| What you get | The continuously-updated answer, assembled into one ranked result. Close to live, and returned in one round trip. | Re-verifies the named jurisdiction(s) in-request, then merges the fresh results with the Regular answer through the same matcher. |
| Speed | One round trip | ≤2 sources may run inline; more → asynchronous |
| Price | Base per-call rate only | Base rate plus $0.02 per completed live source, to a $2.00 ceiling per search |
Regular — the default, and what almost every caller wants
The Regular answer reads the continuously-updated data we hold, assembled into one ranked result, and returns in a single round trip. It is close to live — current enough for the great majority of screening decisions — and it is the mode you get when you say nothing. Every record still carries its own lastCheckedAt, so currency is a value you read off the answer rather than an assumption about the service.
- No
liveblock, nousageblock — a Regular search is billed as a single call at the base per-call rate. statusis"complete". Every jurisdiction the request touched still reports its own row insourceStatus[], so an incomplete search is labelled, never returned as a silently short list.- Each record carries
sources[].lastCheckedAt— when we last confirmed that person at that jurisdiction. There is no separate freshness flag; the timestamp is the answer.
Live verification — checked at the source, right now
A live check is interactive and real-time — the mode to reach for when a decision needs a current, moment-of-decision verification of a specific person. Send a live block and the API re-verifies at the named jurisdiction(s) as part of your request, then merges the fresh results with the Regular answer:
{
"query": { "firstName": "John", "lastName": "Doe", "dob": "1980-04-12" },
"live": { "jurisdictions": ["TX"], "scope": "matched" }
}jurisdictions: [codes]— an explicit list of jurisdiction codes to re-verify live.scope: "matched"— re-verify every jurisdiction that produced a Regular hit for this query.
A live-verified record carries a fresh lastCheckedAt; the merge lets a live result supersede the Regular copy of the same person, and the whole set is re-ranked. A person only the live pull found is added; a person only the Regular answer held stays — nothing is lost.
records array as “not found”, check counts.sourcesIncomplete == 0 first — > 0 means at least one source could not be searched to the end. Full discipline in Result completeness.Sync vs async — a latency rule, not a product difference
A live check of ≤2 sources runs synchronously — one 200, results inline. More than two sources runs asynchronously: a 202 with a searchId and a poll URL. Both paths run the same live checks and return the same envelope — the only difference is whether you hold the connection open or collect the result later. Regular searches and synchronous live searches never produce a job to poll. See Async search & webhooks for the poll handshake.
What a live check costs
- +$0.02 per live source that completes. A Regular search has no such charge — it is the base per-call rate only.
- The live charge for one search never exceeds $2.00. This is a price ceiling, not a source limit — you may request live checks across more sources than the ceiling covers and all of them run; past the ceiling the marginal live check is free.
- A live check that fails is not billed. Only sources that complete are metered, and the
usageblock always reports the true count even when the charge is capped.
"usage": { "liveChecks": 2, "liveChargeUsd": 0.04, "capped": false,
"display": "2 live checks · $0.04" }Currency is answered per request, not per claim
Two fields carry it, and both are on every response. sources[].lastCheckedAt is when that jurisdiction’s copy of the record was last confirmed, and sourceStatus[].lastCheckedAt is the same question at the jurisdiction level for every jurisdiction the search touched — including the ones that returned no match. Read them rather than a general statement about the dataset: they are contract fields, and they answer the question for the exact record and the exact request in front of you.
{
"source": {
"jurisdiction": "NJ",
"registryName": "State Sex Offender Registry",
"recordUrl": "https://…",
"scrapedAt": "2026-08-13T04:12:00Z",
"lastCheckedAt": "2026-08-13T04:12:00Z",
"sourceUpdatedAt": "2026-08-11T00:00:00Z"
},
"sources": [ /* one entry per corroborating jurisdiction */ ]
}sourceUpdatedAt is the date the jurisdiction itself states it last changed the record; it is null where a jurisdiction publishes no such date. Running at volume? POST /v1/searches has no request-timeout ceiling, so every named jurisdiction runs to completion.
List sources
The coverage catalog: every jurisdiction we cover, by code, name, and live health.
The live coverage catalog: every jurisdiction the API covers, with its code, display name, and health signals such as typical latency and last successful refresh. Use it to render your own coverage UI or to decide which jurisdictions to name.
- Coverage. Every jurisdiction with its code and name.
- Health. Typical latency and last successful refresh timestamps, per jurisdiction.
curl https://api.offendersearch.app/v1/sources \
-H "X-API-Key: $OFFENDERSEARCH_KEY"[
{
"id": "NJ",
"name": "State Sex Offender Registry",
"covers": ["NJ"],
"health": { "lastSuccessAt": "2026-07-25T09:14:00Z", "typicalLatencyMs": 380 }
},
{
"id": "CA",
"name": "State Sex Offender Registry",
"covers": ["CA"],
"health": { "lastSuccessAt": "2026-07-25T09:14:00Z", "typicalLatencyMs": 410 }
}
]