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
}'Criminal search API request body
The person goes inside query. Paging, live checks and response options sit beside it at the top level.
| Field | Status | What it does |
|---|---|---|
query | Required | The person you are searching for. lastName is required inside it. |
page | Optional | Top level, not inside query. Defaults to 1. |
perPage | Optional | Top level. Defaults to 50, between 1 and 200. |
live | Optional | Switches on live verification: { "jurisdictions": [...] }, { "scope": "matched" } and/or { "websites": [...] }. |
include | Optional | ["sourceData"] adds the per-source annex the default response leaves out. omittedFields tells you what was held back. |
Criminal search API parameters
Everything inside query. The surname is the anchor; every other field narrows within it.
| Field | Status | What it does |
|---|---|---|
lastName | Required | Every query is bounded by a surname. A search without one is refused with 422 guard_unbounded_query rather than answered from an arbitrary slice. |
firstName | Strongly recommended | Given name. Nicknames and common variants are matched, so “Bob” finds “Robert”. |
dob | Recommended | YYYY-MM-DD. The strongest disambiguator. One dob is compared against full dates, birth years and ages, whichever each record publishes. |
birthYear / birthMonth / age | Optional | Use when a full date of birth is unknown. age is matched within one year. |
state | Optional | Two-letter code, e.g. TX. A location filter on the results. |
county | Optional | Narrow within a state to one county. Also accepted as jurisdiction. |
city / zipcode / address | Optional | Match on a record’s residential address. Only records that carry one can match. |
nameStrategy | Optional | prefix (default) or exact, for the surname. |
prefixMatch | Optional | Explicit prefix matching on lastName, firstName or both. Needs at least three characters, or the call returns 422 prefix_too_short. |
website | Optional | A 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.
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 kind | Jurisdiction code | recordKind filter | In the catalog |
|---|---|---|---|
| County jail booking | <ST>-<COUNTY> | jail-roster | 1,375 |
| State prison record | <ST>-DOC | doc-inmate | 53 |
| Court case | <ST>-COURT | court | 24 |
| Warrant | <ST>-WARRANT | warrant | 28 |
| Registry | <ST>-REGISTRY | registry | 11 |
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.
Every answer says what it covered
counts.sourcesQueriedandsourcesComplete— how many jurisdictions a live check reached, and how many answered in fullcounts.sourcesIncomplete— above zero means the result is a lower bound, said out loud rather than silently droppedsources[].recordUrlon every record, so a result can be verified at the jurisdiction that holds itmatchState,matchConfidenceandmatchBasison every match — a name is matched and explained, not string-comparedsources[].codeon every record, so an arrest is never mistaken for a conviction in your own code
Full field reference: the record object.
Pagination: page, perPage and totalPages
A common surname can return hundreds of records. Page through them with two top-level fields.
// 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.
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.
{
"query": { "lastName": "Hamilton", "state": "TX", "dob": "1989-04-23" },
"live": { "jurisdictions": ["TX-DALLAS"] }
}Details: live and async.
Criminal search API errors
Errors share one envelope: error.code for your code to branch on and error.message for a person to read.
| Status | Code | When |
|---|---|---|
| 401 | unauthenticated | The X-API-Key header is missing or malformed. |
| 404 | not_found | No search or record carries that id, or it belongs to another account. |
| 413 | batch_too_large | A batch of more than 1,000 rows. |
| 422 | guard_unbounded_query | query.lastName is missing. |
| 422 | prefix_too_short | An explicit prefix search below three characters. |
| 422 | no_live_sources | A live request that resolved to no sources. |
| 422 | bad_batch / empty_batch | An unparseable batch body, a CSV with no header row, or zero rows. |
// { "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 other criminal API endpoints
| Endpoint | What it is for |
|---|---|
POST /v1/criminal/search | Search one person. This page. |
GET /v1/criminal/searches/{searchId} | Poll an asynchronous live search. |
POST /v1/criminal/batch | A JSON list or CSV of up to 1,000 people, one result per row. See bulk criminal record search. |
GET /v1/criminal/jurisdictions | List jurisdictions, filtered by state, recordKind or live. |
GET /v1/criminal/sources | The public coverage catalog: every jurisdiction, live or pending. |
POST /v1/criminal/resolve | Turn 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.
Criminal search API pricing
Metered per call. No setup fee, no seats, no monthly minimum on search.
| Item | Price |
|---|---|
| Criminal search (cached answer) | $0.15 per search |
| Volume rate | $0.11 per search past 2,000 searches in a calendar month |
| Free to start | 25 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 workflows | Contact us for pricing |
Worked examples by volume: background check API pricing · all rates: pricing.
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
- Sex offender API — One call across 58 US sex offender registries, with scored matches and a citation on every record.
- Sex offender search API — Endpoints, request shapes, record fields, matching and batch, in detail.
- Sex offender database — The national registry data in one schema — photos, aliases, offenses, addresses.
- Registered offender database — What a registered offender record holds, and how its accuracy is measured.
- Bulk sex offender search — Screen a whole list of names in one upload or one API call.
- Sex offender registry API — Choose which of the 58 registries a search covers, and see which ones answered.
- Offender search API — The registry search and the criminal search side by side, on one key.
Criminal records
- Criminal records API — County, state and federal records — jail, prison, court and warrant — through one API.
- Criminal background check API — One cited answer per person for onboarding and screening flows.
- Court records API — Criminal court cases with charges and a disposition on each charge.
- Arrest records API — County jail bookings: booking date, custody status, facility and photo link.
- Inmate search API — State prison and county jail custody, with sentence, release and parole dates.
- Background check API — The registry and criminal layers together, on one key and one bill.
- Employment background check API — The registry and criminal data layer for HR, ATS and screening platforms.
- Employment screening API — The stages of a hiring pipeline, and where criminal and registry checks sit in it.
- Bulk criminal record search — Run a list of names against criminal records in one call.
- Statewide criminal search — One state at a time: prison, court and warrant systems that cover the whole state.
- Federal criminal records — Federal criminal records through the same API, and what a federal search covers.
Build with it
- API documentation — Quickstart, every endpoint, the record object and the OpenAPI spec.
- Sex offender map API — Clusters, pins and names for your own map, and the full record when one is opened.
- Background check API pricing — $0.15 a call, $0.11 past 2,000 a month, 25 free calls to start — worked out by volume.
- Background check API integration — Keys, the first call, async and webhooks, batch, monitoring and errors, step by step.
- Free background check API — 25 free calls on real data, no card — what is included and what comes after.
- Pay-as-you-go background check API — Per call, per report or subscription: the pricing models, with costs by volume.
- Background check API in Python — A working tutorial with requests: search, batch, async, errors and retries.
- Background check API in Node.js — A fetch client, response types, an Express webhook handler and retries.
- NSOPW API — Is there one? What exists, and a developer API for searching US registries.
- MCP server for offender search — Give Claude and other AI agents the same search as a tool.
- Integrations — Screening inside referral software — CarePort, ExtendedCare, Ensocare and Aidin.
- AI integration — Offender safety checks for AI agents and chatbots, with cited answers.
- Offendersearch vs offenders.io — A side-by-side comparison of the two registry APIs.
Screening and compliance
- Tenant background check — Screening applicants for a rental, and where an FCRA workflow is required.
- FCRA Partner Access — The FCRA workflow for hiring and tenant decisions — pricing by quote.
- FCRA-compliant background check data — Accuracy, disputes and permissible purpose for consumer reporting agencies.
- FCRA disputes API — Open, track and resolve a consumer dispute against a record.
- Sex offender monitoring — Watch a person or an address and get an alert when the registry changes.
- Tenant screening API — The registry and criminal layer for property software, with FCRA workflows for rental decisions.
- Non-FCRA background check API — What a public-records lookup may be used for, and when a decision needs FCRA instead.