Developer docs · v1.0.0

Getting started in five minutes

Key, environment, one request, one response. Then the product quickstart that fits your question.

The seven steps

  1. 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.
  2. Select your environment. Use a test key (os_test_…) against https://sandbox.offendersearch.app to integrate — fixed test names, never billed. Use a live key (os_live_…) against https://api.offendersearch.app for real searches, which are billed. See Sandbox & production.
  3. Copy a working request. The three examples below search the sex offender registries for one person. Swap /v1/search for /v1/criminal/search to search criminal records instead.
  4. Execute it. A 200 carries the full answer. status is complete or partial; counts.records is how many people matched; records[] are the matches, best first.
  5. Understand the result. Read status and sourceStatus[] before records[]: an empty records[] is only an answer when every source finished. Each record carries matchConfidence and, with a date of birth, a matchState saying how strongly the birth date agreed.
  6. Handle errors. 401 means the key is wrong or missing; 422 means the query was refused (read detail); 429 means slow down (Retry-After). See Errors, rate limits & retries.
  7. 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.

Next