Getting started in five minutes
Key, environment, one request, one response. Then the product quickstart that fits your question.
The seven steps
- Get your API key. Create an account at offendersearch.app/sign-up, open Dashboard → API keys and create a key. The secret is shown once. Store it as
OFFENDERSEARCH_API_KEY. - Select your environment. Use a test key (
os_test_…) againsthttps://sandbox.offendersearch.appto integrate — fixed test names, never billed. Use a live key (os_live_…) againsthttps://api.offendersearch.appfor real searches, which are billed. See Sandbox & production. - Copy a working request. The three examples below search the sex offender registries for one person. Swap
/v1/searchfor/v1/criminal/searchto search criminal records instead. - Execute it. A
200carries the full answer.statusiscompleteorpartial;counts.recordsis how many people matched;records[]are the matches, best first. - Understand the result. Read
statusandsourceStatus[]beforerecords[]: an emptyrecords[]is only an answer when every source finished. Each record carriesmatchConfidenceand, with a date of birth, amatchStatesaying how strongly the birth date agreed. - Handle errors.
401means the key is wrong or missing;422means the query was refused (readdetail);429means slow down (Retry-After). See Errors, rate limits & retries. - Go live. Switch the host and the key; nothing else changes. Run the production checklist.
A working request
curl -X POST https://api.offendersearch.app/v1/search \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": {"firstName": "Jordan", "lastName": "Rivera", "dob": "1988-04-12"}}'Jordan Rivera is a fictional name. In the sandbox, use the fixed test names on each product's sandbox page to get deterministic results: Sex Offender, Criminal, Monitoring, Map, FCRA.
The response, in brief
{
"searchId": "srch_1f2e3d4c5b6a79880a1b",
"status": "complete",
"elapsedMs": 412,
"counts": { "records": 1, "recordsReturned": 1, "sourcesQueried": 58, "sourcesComplete": 58, "sourcesIncomplete": 0 },
"warnings": [],
"sourceStatus": [ { "source": "TX", "status": "ok", "matched": 1, "fromCache": true, "incomplete": false, "incompleteReason": null } ],
"records": [
{
"recordId": "rec_3f9a1c2b4d5e6f708192",
"matchConfidence": 0.96,
"matchBasis": ["lastName:exact", "firstName:exact", "dob"],
"matchDetail": { "strategies": { "lastName": "exact", "firstName": "exact" }, "fieldsPresent": ["lastName", "firstName", "dob"], "verification": "dob_match" },
"matchState": "dob_match",
"name": { "first": "Jordan", "middle": "", "last": "Rivera", "suffix": "", "full": "Jordan Rivera" },
"dob": "1988-04-12",
"dobPrecision": "exact",
"registrationState": "TX",
"offenses": [ { "crime": "…", "statute": "…", "convictionDate": "…" } ],
"sources": [ { "jurisdiction": "TX", "registryName": "Texas Public Sex Offender Registry", "recordUrl": "https://…", "lastCheckedAt": "2026-10-24T03:10:00+00:00" } ]
}
]
}Field names are exact; the values are illustrative. A Regular search like this one carries no meterEvents[] (it is one call, reported in the dashboard); usage and meterEvents appear when a live block ran. The complete field list is in each product's data reference: Sex Offender Record, Criminal Record.
What this costs
A sandbox request costs nothing. A live-host search is one billable call, counted in Dashboard → Usage; when a search also charges live sources or documents, the response itemises those in meterEvents[]. Live re-checks of named sources, evidence documents and monitors are separate meters; each product's pricing is on its billing or quickstart page and on pricing.