OffenderSearch
Criminal API Reference

Criminal Search API reference

Every endpoint and every object, each linked to its request and response fields.

Base URL https://api.offendersearch.app

How the criminal records API reference is organised

Every endpoint documents what it is sent and what it returns in two tables, next to the guide that explains it: POST /v1/criminal/search on Search, the async poll on Async & long-running, the batch on Batch & CSV, and so on. Shared objects — the CriminalRecord that search, batch and the record lookup return, its cases and charges, the live SourceStatus, the Error body — are defined once and linked from every table that carries them. The same tables are in the markdown twin of each page.

Criminal search API endpoints — request and response fields

Each endpoint links to its reference block: a Request table (path, query and body fields, with type, required and default) and a Response table (every field, with type and when it is null). Every endpoint takes the X-API-Key header except GET /v1/criminal/sources.

EndpointWhat it does
POST /v1/criminal/searchSearch. Search criminal records for one person. Without a live block it returns the cached answer; with one it re-verifies the named jurisdictions at the source during the request. Bills one criminal_call, plus $0.02 per completed live source up to $2.00.
GET /v1/criminal/searches/{searchId}Get an async search. Read an async live job. Returns 202 while it runs and 200 with the full envelope in result once every source has an outcome. Polling is free.
POST /v1/criminal/batchBatch. Run up to 1,000 cached searches in one request, one result per row in input order. Each row that runs bills one criminal_call. Live verification is not available in a batch.
GET /v1/criminal/records/{recordId}Get a record. Fetch one current record by its externalId, sending its sources[].code as source so the lookup is a direct key read. The record comes back unscored: matchConfidence is 0.0 and the match fields are empty, because there is no query to score against. Not billed.
GET /v1/criminal/sourcesList sources. The coverage catalog: every source by public code, whether it is available, and when its records were last updated. No API key needed, so coverage can be checked before you sign up. Not billed.
GET /v1/criminal/jurisdictionsList jurisdictions. Every covered jurisdiction by public code, state, county and record kind, filtered by the query parameters. Not billed.
POST /v1/criminal/resolveResolve a website. Map a roster or records website you already use to the public jurisdiction code that covers it, or say it is not covered. Not billed.
GET /v1/criminal/historySearch history. Your account's 50 most recent criminal searches, newest first, with the searchId a report needs. Not billed.
POST /v1/criminal/reportVerification report. Render a PDF that records a search was run: the criteria, the sources searched and the records found (for a stored searchId, the page that search returned), with the not-a-consumer-report notice. A report on a stored searchId bills only the report; an inline query bills the search too.

Criminal search API objects — CriminalRecord, Case, SourceStatus and more

Each object is defined once, on the page linked here. Wherever an endpoint or another object carries it, the type links back to that one definition.

ObjectWhat it is
ErrorThe body of every error the criminal service returns: one nested error object with a stable code to branch on and a message to log or show. A 401 or 402 comes from the gateway in the GatewayError shape instead.
GatewayErrorThe body of a 401 (missing or invalid X-API-Key) or a 402 (free trial used up, or no active subscription). The gateway answers these before the criminal service runs, so the shape is the Sex Offender API's flat detail string.
QueryThe person you are looking for. lastName is the only required field; every other field narrows within that surname. Send it as query on POST /v1/criminal/search and POST /v1/criminal/report, or as one row of queries[] on POST /v1/criminal/batch. The fields can also be sent flat at the top level of the body.
LiveRequestThe live block. Its presence turns on live verification: the named jurisdictions are re-checked at the source during the request and the fresh results merge with the cached answer through the same matcher. Up to 2 sources that can answer quickly run synchronously (200); more, or any source that cannot, run as an async job (202).
CountsTotals on every criminal search. The three live counts are 0 on a cached search; sourcesOnFile and sourcesPending describe the sources behind the answer on every search.
SearchCoverageWhich sources stand behind a search for the state or county searched, and which sources for that scope are not available yet. Codes are the public codes GET /v1/criminal/sources lists. It does not change status.
SearchCoverageSourceOne source in a search's coverage block.
SourceStatusOne live source's outcome within a search. Present only on a live search; a cached search returns sourceStatus: [].
ResolutionEntryWhat one website resolved to, as reported in a search's resolution[]. The same fields as a ResolveResult without state and county.
UsageThe live-check count and charge, present only on a live search. The count is the true number of completed checks even when the charge is capped.
MeterEventOne billable unit this request emitted.
LegalThe notice carried on every criminal search response and every batch row.
CriminalRecordOne record from one jurisdiction, as search, batch and the record lookup return it. A person found in several jurisdictions (booked in one county, a court case in another, an inmate record statewide) comes back as one record per jurisdiction; relate them by name, dob and state. Every key is present on every record. Empties mean different things: "" is a text field the source does not publish, null is a date, number, object or demographic that is not known, and [] is a list with nothing on file.
NameA name in four parts, each "" when not published. There is no full key; join the parts for display. Casing is as published; matching ignores case.
DobAll birth evidence on a record. dobPrecision says which parts are real; date is a full date or null, never a made-up day. A source's placeholder date (1/1 of a year with nothing else to support it, and the like) never becomes a full date; a sentinel (1900-01-01, 0001-01-01, any 9999 date, a year before 1900 or after this year) is no birth evidence, so birthYear is null too.
AddressA residence, in the same slots as the Sex Offender API's address. Text fields are "" when not published.
CaseA court case and its charges. filedDate is ISO or null; text fields are "" when not published.
SentenceA sentence. Text fields are "" when not published; dates are ISO or null.
ParoleParole posture as published. Dates are ISO or null; text fields "" when not published.
CustodyCustody classification, alongside currentStatus and custodyFacility. Text fields "" when not published.
ChargeOne charge on a case. severity: null means the source did not classify it, not that it is minor.
SourceRefThe jurisdiction a record comes from. code names a state and a place, nothing else.
LifecycleRecords are never deleted when a person leaves a source; the departure is reported here and the record is still returned.
CoverageCoverage totals for the whole product, by jurisdiction.
SourceInfoOne source in the coverage catalog.
JurisdictionInfoOne covered jurisdiction: its public code and its place, nothing else.
ResolveResultWhat one website resolved to. The response names the state, the county and the public code, never the website's host.
ResolveListResponseThe answer when websites (a list) is sent.
JobRecordAn async live job as GET /v1/criminal/searches/{searchId} returns it: pending with result: null until every source has an outcome, then complete with the full search envelope in result.
HistoryEntryOne prior criminal search. Pass searchId to POST /v1/criminal/report to render it.
BatchRowOne row's result. A row that ran is the search envelope without searchId, meterEvents, usage and resolution, plus index. A row that failed validation carries only index, status: "error", error, zeroed counts and records: [].
ClearedEntryOne record a not_this_person clearance removed from records[] for the subjectId sent. cleared[] carries one of these per suppressed record, so an interface can say how many previously cleared people are hidden and link each to its clearance.
RecordClearanceThe clearance that names a record's person, attached as clearance when the request carried subjectId and the record was kept: in clearanceMode: "annotate", on a this_person verdict, or because a cleared person has re-surfaced (staleReason set). A record no clearance names carries no clearance key.

Product overview