Offendersearch
One endpoint · five record kinds

The Criminal Search API

The criminal search API is one endpoint, POST /v1/criminal/search. One call reaches more than a thousand county, state and federal jurisdictions — jail rosters, prison systems, court records, warrants and registries — and returns one schema with a citation on every record.

Same key as the registry API. $0.15 a call, $0.11 past 2,000 a month. Searching a whole list? Bulk criminal record search →

curl -X POST \
  https://api.offendersearch.app/v1/criminal/search \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "firstName": "Alex",
      "lastName": "Hamilton",
      "dob": "1989-04-23",
      "state": "TX"
    },
    "page": 1,
    "perPage": 50
  }'
The request

Criminal search API request body

The person goes inside query. Paging, live checks and response options sit beside it at the top level.

FieldStatusWhat it does
queryRequiredThe person you are searching for. lastName is required inside it.
pageOptionalTop level, not inside query. Defaults to 1.
perPageOptionalTop level. Defaults to 50, between 1 and 200.
liveOptionalSwitches on live verification: { "jurisdictions": [...] }, { "scope": "matched" } and/or { "websites": [...] }.
includeOptional["sourceData"] adds the per-source annex the default response leaves out. omittedFields tells you what was held back.
Parameters

Criminal search API parameters

Everything inside query. The surname is the anchor; every other field narrows within it.

FieldStatusWhat it does
lastNameRequiredEvery query is bounded by a surname. A search without one is refused with 422 guard_unbounded_query rather than answered from an arbitrary slice.
firstNameStrongly recommendedGiven name. Nicknames and common variants are matched, so “Bob” finds “Robert”.
dobRecommendedYYYY-MM-DD. The strongest disambiguator. One dob is compared against full dates, birth years and ages, whichever each record publishes.
birthYear / birthMonth / ageOptionalUse when a full date of birth is unknown. age is matched within one year.
stateOptionalTwo-letter code, e.g. TX. A location filter on the results.
countyOptionalNarrow within a state to one county. Also accepted as jurisdiction.
city / zipcode / addressOptionalMatch on a record’s residential address. Only records that carry one can match.
nameStrategyOptionalprefix (default) or exact, for the surname.
prefixMatchOptionalExplicit prefix matching on lastName, firstName or both. Needs at least three characters, or the call returns 422 prefix_too_short.
websiteOptionalA roster or records URL; the search is scoped to the jurisdiction it resolves to.

A record is dropped only when it has data that conflicts with your query. One that is missing the field you sent is kept and flagged. Full rules: search reference and matching.

What you integrate against

One schema over five record kinds

A jail booking, a prison term, a court case and a warrant are four different shapes at the jurisdictions that hold them. They arrive here as one object. recordType is always "criminal_record"; the kind is told by the jurisdiction code in sources[].code.

Record kindJurisdiction coderecordKind filterIn the catalog
County jail booking<ST>-<COUNTY>jail-roster1,375
State prison record<ST>-DOCdoc-inmate53
Court case<ST>-COURTcourt24
Warrant<ST>-WARRANTwarrant28
Registry<ST>-REGISTRYregistry11

The recordKind values filter GET /v1/criminal/jurisdictions, not the search itself — see jurisdictions. Each kind has its own page: arrest records API, inmate search API and court records API.

Built to be auditable

Every answer says what it covered

  • counts.sourcesQueried and sourcesComplete — how many jurisdictions a live check reached, and how many answered in full
  • counts.sourcesIncomplete — above zero means the result is a lower bound, said out loud rather than silently dropped
  • sources[].recordUrl on every record, so a result can be verified at the jurisdiction that holds it
  • matchState, matchConfidence and matchBasis on every match — a name is matched and explained, not string-compared
  • sources[].code on every record, so an arrest is never mistaken for a conviction in your own code

Full field reference: the record object.

Paging

Pagination: page, perPage and totalPages

A common surname can return hundreds of records. Page through them with two top-level fields.

Paging
// page and perPage are TOP-LEVEL — not inside query.
{ "query": { "lastName": "Hamilton", "state": "TX" }, "page": 3, "perPage": 200 }

// → counts.records is the total before the slice; totalPages is authoritative.
{ "page": 3, "perPage": 200, "totalPages": 3, "counts": { "records": 512 } }

perPage defaults to 50 and is clamped to 1–200. Sending page or perPage inside query has no effect. Details: pagination.

Two modes

Cached search and live verification

There is one endpoint. The request body decides which answer you get.

Leave live out and you get the cached answer: one indexed query, typically sub-second. Add a live block and the named jurisdictions are re-checked during the request, with each record marked liveChecked. More than two live sources run asynchronously — the call returns a searchId and a poll URL instead of waiting.

Live verification
{
  "query": { "lastName": "Hamilton", "state": "TX", "dob": "1989-04-23" },
  "live": { "jurisdictions": ["TX-DALLAS"] }
}

Details: live and async.

Errors

Criminal search API errors

Errors share one envelope: error.code for your code to branch on and error.message for a person to read.

StatusCodeWhen
401unauthenticatedThe X-API-Key header is missing or malformed.
404not_foundNo search or record carries that id, or it belongs to another account.
413batch_too_largeA batch of more than 1,000 rows.
422guard_unbounded_queryquery.lastName is missing.
422prefix_too_shortAn explicit prefix search below three characters.
422no_live_sourcesA live request that resolved to no sources.
422bad_batch / empty_batchAn unparseable batch body, a CSV with no header row, or zero rows.
422 Unprocessable Entity
// { "query": { "firstName": "Alex", "state": "TX" } }   → HTTP 422
{ "error": { "code": "guard_unbounded_query",
             "message": "a lastName predicate is required — an unbounded query returns a lottery, not an answer" } }

A 422 is a deliberate refusal, not a fault — fix the request and retry. Full list: errors.

The rest of the surface

The other criminal API endpoints

EndpointWhat it is for
POST /v1/criminal/searchSearch one person. This page.
GET /v1/criminal/searches/{searchId}Poll an asynchronous live search.
POST /v1/criminal/batchA JSON list or CSV of up to 1,000 people, one result per row. See bulk criminal record search.
GET /v1/criminal/jurisdictionsList jurisdictions, filtered by state, recordKind or live.
GET /v1/criminal/sourcesThe public coverage catalog: every jurisdiction, live or pending.
POST /v1/criminal/resolveTurn a roster or records URL into the jurisdiction code that covers it.

The same call, explained as a screening flow: criminal background check API. The record layers it searches: criminal records API. Registry and criminal together: background check API.

Pricing

Criminal search API pricing

Metered per call. No setup fee, no seats, no monthly minimum on search.

ItemPrice
Criminal search (cached answer)$0.15 per search
Volume rate$0.11 per search past 2,000 searches in a calendar month
Free to start25 searches, no card
Live re-verification+$0.02 per completed source, capped at $2.00 per search; a failed live check is not billed
Batch (CSV or JSON list)Each row billed as one search
FCRA workflowsContact us for pricing

Worked examples by volume: background check API pricing · all rates: pricing.

FAQ

Criminal search API questions

What does the criminal search API endpoint do?

POST /v1/criminal/search takes one person — a surname at minimum — and returns every matching criminal record in one JSON schema: jail bookings, state prison records, court cases, warrants and registry entries. Each record says which jurisdiction holds it and how strongly it matched.

How is this different from the sex offender registry API?

Different records, same everything else. It runs on the same account, the same X-API-Key, the same scored-match model and the same billing — $0.15 a call, $0.11 past 2,000 a month. If you have already integrated the registry search, the criminal endpoints need no new credential and no new contract.

Why is a last name required?

Because a criminal search without a surname has no honest answer. A first name, a date of birth or a state on its own matches an enormous number of people, and returning the first page of them would look like a result while being a lottery. The API refuses the call with 422 guard_unbounded_query instead.

How do I know which jurisdictions a search actually reached?

Every response carries a counts block and a sourceStatus list, and every record carries the code of the jurisdiction that holds it plus that jurisdiction’s own record link. sourcesIncomplete above zero means a source did not answer in full, so the result is a lower bound — said out loud rather than silently dropped.

Can I discover coverage programmatically?

Yes. GET /v1/criminal/sources returns every jurisdiction in the catalog, each marked live or pending, with its scope. GET /v1/criminal/jurisdictions filters the same list by state and recordKind. Build against those rather than a number on a marketing page.

Is there a batch endpoint?

Yes. POST /v1/criminal/batch takes a JSON list or a CSV of up to 1,000 rows, and each row is billed as one call. Rows that error are reported with a reason and are not billed. Full detail is in the batch reference.

Can I use this for FCRA-regulated screening?

Yes, through FCRA Partner Access — FCRA orders for approved consumer reporting agencies and screening platforms, under a written FCRA agreement, where one order covers a criminal search, a registry search or both. Standard results are not a consumer report.

Run your first criminal search

25 free searches, no card. The same key runs the sex offender registry search.

The Offendersearch APIs

One account, one key, one bill — the sex offender registry and criminal records, side by side.

Sex offender registry

Criminal records

Build with it

Screening and compliance