Criminal record verification API
Confirm a criminal record with the jurisdiction that holds it before it goes on a report. One live block on the search you already send; $0.02 per source that answers, never more than $2.00 a search, with the citation and the check time on each record.
Updated November 8, 2026 · API changelog
{
"query": { "firstName": "Felony", "lastName": "Record", "dob": "1975-06-15" },
"live": {
"jurisdictions": ["US-DOC"], // the systems to ask again, by code
"evidence": "pdf" // keep a document of what each one answered
}
}curl -X POST "https://sandbox.offendersearch.app/v1/criminal/search" \ -H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": { "firstName": "Felony", "lastName": "Record", "dob": "1975-06-15" } }'
A verified criminal record in JSON, from the sandbox
The same search as the hero, with live.jurisdictions set to the federal prison system. Read sourceStatus first: it says who was asked and who answered.
{
"status": "complete",
"counts": {
"records": 1,
"recordsReturned": 1,
"sourcesQueried": 1,
"sourcesComplete": 1,
"sourcesIncomplete": 0,
"candidatesCapped": false,
"exclusionsIncluded": false,
"sourcesOnFile": 1934,
"sourcesPending": 197
},
"warnings": [],
"page": 1,
"perPage": 50,
"totalPages": 1,
"sourceStatus": [
{
"code": "US-DOC",
"source": "US-DOC",
"ok": true,
"incomplete": false,
"incompleteReason": null,usage block at the end is the bill for the live part: one completed check, $0.02. The sandbox person’s stored record is a county court case, so the federal system reports records: 0 and live: true: a defensible “not held there” rather than a blank.Run it yourself with a test key ›| Field | Type | What it tells you |
|---|---|---|
| sources[].code | string | The jurisdiction that holds the record, as a neutral code (US-DOC, OH-SAMPLE). The same code goes in live.jurisdictions. |
| sources[].level | string | county, state or federal: which tier of system answered. |
| sources[].recordUrl | string | The jurisdiction’s own page for the record, so a reviewer can open the original. |
| sources[].lastCheckedAt | datetime | When that system was last asked about this record. On a live check it is the moment of your request. |
| sources[].live | boolean | true when this answer came from the jurisdiction during your request; false when it is the stored copy. |
| sourceStatus[] | array | One row per system named in live.jurisdictions: ok, incomplete, incompleteReason, records and live: true. |
| dobVerification | enum | record when the record carries birth evidence; source_confirmed when the jurisdiction confirmed your date on a live check; source_not_confirmed when it did not. |
| usage.liveChecks, usage.liveChargeUsd | number | How many live sources completed and what they cost on this search; a source that did not answer is not counted. |
| lifecycle.droppedOut | boolean | Whether the jurisdiction has stopped listing the record. The record is kept and flagged, never silently removed. |
| personRef | string | A stable handle for the person across searches and monitors, so a re-verification months later can be tied to the same subject. |
How live re-verification of a criminal record works
Three steps inside one request. The cached answer is the baseline; the live block tells the API which systems to ask again, and the merged response says which ones did.
- The cached search runs first. Name, date of birth and the places you hold go in
query; the stored records come back scored withmatchStateandmatchConfidence. This is the $0.15 call. - The live block names what to re-check.
live.jurisdictionsis a list of codes;live.scope: "matched"means “whichever systems hold a matching record”. Each named system is asked during your request, and its own candidates are scored against your query. - The answers are merged and labelled. A record confirmed live carries
sources[].live: trueand a newlastCheckedAt; a system that did not answer isincompleteinsourceStatusand not billed. Up to two in-request systems answer inline (200); more run as a job you poll (202).
| You asked for | What you are billed |
|---|---|
| The cached search only | $0.15 a call |
| Live, 1 system answered | $0.15 + $0.02 |
| Live, 3 named, 2 answered | $0.15 + $0.04 |
| Live, 120 answered | $0.15 + $2.00 (the ceiling) |
A failed live check is reported, not hidden, and not billed. The reference: live and async searches.
Provenance on each record: which system, which tier, which page, when
A CRA’s reviewer needs to open the original and write down when it was checked. Both are on the record, not in a separate report.
Each record lists the systems that hold it in sources[], and each entry names the jurisdiction by a neutral code, its tier (county, state, federal), the jurisdiction’s own page for the record and the time it was last asked. A person held by a county jail and later a state prison comes back as one record whose sources list both systems, each with its own link.
externalId is the jurisdiction’s own number prefixed with its code; personRef is a stable handle for the person that survives a renumbered listing, and the key a continuous criminal monitoring API alert carries back to the subject you verified.
| Question a reviewer asks | Field that answers it |
|---|---|
| Where does this record come from? | sources[].code, sources[].level |
| Can I see the original? | sources[].recordUrl |
| When was it last confirmed? | sources[].lastCheckedAt |
| Was it confirmed during my request? | sources[].live, liveChecked |
| Is it still listed by the jurisdiction? | lifecycle.presentInLastSweep, lifecycle.droppedOut |
| Did the jurisdiction confirm my date of birth? | dobVerification |
| Is this the same person as last time? | personRef, externalId |
Why the date of birth decides whether a hit is an identification: date of birth in the criminal search.
Evidence a background check provider can hand to a client
Three artefacts, each from a documented endpoint, each priced. The one your client can check without trusting you is the signed receipt.
| Artefact | How you get it | Price | Who can check it |
|---|---|---|---|
| Verification PDF of a search | POST /v1/criminal/report with the searchId, within 7 days. Lists the criteria, each matching record and a source citation per record. | $0.02 a document | Your reviewer and your client; the search was already billed, only the PDF is metered. |
| Registry evidence documents | POST /v1/search with live.evidence: "pdf": one document per named registry, downloaded from GET /v1/searches/{id}/evidence/{jurisdiction}.pdf. Always asynchronous. | $0.25 per completed document, $5.00 ceiling a search | Anyone you send the document to; the live evidence reference explains the hash on each. |
| FCRA report with a signed receipt | GET /v1/fcra/orders/{id}/report?format=pdf for the stored PDF; GET /v1/fcra/receipts/{orderId} for the Ed25519-signed receipt over its fingerprints. | Included in the $4.95 order | Anyone holding the PDF, with no key and no upload, at /verify or with OpenSSL against the published key. |
The receipt is built for hand-offs: a report PDF passes from a screening company to an employer to an applicant, and each hop is a chance for the file to be edited or swapped. The receipt signs the order id, the completion time, the control version and the SHA-256 fingerprints of the report and the PDF; it holds no personal data, so it can be public.
The PDF response carries its fingerprint in an X-PDF-SHA256 header; the JSON report carries an integrity block with reportSha256, pdfSha256, receiptUrl and verifyUrl. A change to the report writes a new revision with its own receipt, and earlier revisions stay fetchable with ?revision=N.
The verification PDF is the lighter artefact for the data API: your report of public-record data, labelled as such, with a source-attribution section and a legal notice. Registry evidence documents belong to the sex offender API on the same key: a registry named in live.jurisdictions on POST /v1/search with evidence: "pdf" produces one document per registry, each with its own billed and chargeUsd.
Disputes and corrections on a verified criminal record
A verification API that cannot take a record back is half a product. Here is the path, endpoint by endpoint, and what each step does to the report.
Under FCRA Partner Access a consumer disputes a furnished record from the consumer portal or your own interface, and your system opens it with POST /v1/fcra/orders/{id}/disputes, naming the record ids, a reason and the consumer’s statement. The record is re-checked with the court or agency that holds it. The outcome arrives within 30 days; the consumer receives a receipt when the dispute opens and the resolution when it closes.
The result changes the report, not just a flag. A dispute resolved verified leaves the report as it was. One resolved modified, deleted or unable_to_verify rebuilds the report without the record, re-renders the PDF, and signs a new receipt for the new revision; order.revised tells your system it happened. Any adverse action pending on the order pauses while a dispute is open and is cancelled if the report it rested on changed.
The FCRA dispute API page documents the dispute object; a person described by a record found through the standard key can report a record problem.
| Step | Endpoint | What changes |
|---|---|---|
| Re-check one search on an order | POST /v1/fcra/searches/{id}/reverify | A new search of the same type supersedes the old one; the order completes again with a new report, PDF and receipt. |
| Open a dispute | POST /v1/fcra/orders/{id}/disputes | The record is re-checked with its jurisdiction; the consumer receives a receipt; adverse action pauses. |
| Read it back | GET /v1/fcra/orders/{id}/disputes | Status, resolution and the record it concerns. |
| A changed report | GET …/report?revision=N | Each revision keeps its PDF and its receipt; the latest is the default. |
| Its receipt | GET /v1/fcra/receipts/{orderId}?revision=N | Public, no key, no personal data; signs the revision number. |
Criminal record verification coverage by state
What a live block can name: county jail rosters, statewide prison systems, statewide courts and warrant systems. A live jurisdiction answers a search today.
The twelve states with the most live jurisdictions. Each state links to its criminal records by state page.
| State | County jail rosters (live / catalogued) | Prison system | Courts | Warrants |
|---|---|---|---|---|
| Kentucky | 119 / 121 | Statewide | Statewide | — |
| Texas | 84 / 104 | Statewide | Statewide | Statewide |
| Ohio | 82 / 86 | Statewide | Statewide | Statewide |
| South Carolina | 72 / 73 | Statewide | Statewide | Statewide |
| Indiana | 70 / 74 | Statewide | Statewide | — |
| Pennsylvania | 69 / 71 | Statewide | Statewide | Statewide |
| California | 66 / 71 | Statewide | Statewide | Statewide |
| New York | 65 / 67 | Statewide | Statewide | Statewide |
| Missouri | 66 / 68 | Statewide | Statewide | — |
| Montana | 61 / 72 | Statewide | Statewide | — |
| Virginia | 60 / 62 | Statewide | Statewide | Statewide |
| Florida | 57 / 63 | Statewide | Statewide | Statewide |
Jurisdictions in our catalogue as of 2026-11-08; a live jurisdiction answers a search today. Approximate, not an official figure. Showing the 12 states with the most live jurisdictions; the full list is in the criminal coverage reference.
A verification can only confirm a record with a system that publishes it; coverage.sourcesPending on each response counts the catalogued systems not yet answering. A confirmation is also a statement about one moment, which is what a monitor is for afterwards.
Data API or FCRA Partner Access for a criminal record verification
The records and the live check are the same on both paths. What differs is whether the result may be furnished for a decision about a person.
Data API, not FCRA
- For: visitor and volunteer screening, trust and safety, research, monitoring, and software that shows a public record to the person who asked for it.
- Must not decide: whether someone is hired, housed, insured or given credit. The response is a public-records lookup, not a consumer report, and the key is not approved for those uses.
- Example: An admissions desk screens a visitor against the registries before a visit; no hiring, housing or credit decision is involved.
Self-serve: get a key, then POST /v1/search or /v1/criminal/search.
FCRA Partner Access
- Built in: end-user registration with a permissible purpose, consent on every order, the pre-adverse and adverse-action notices with the consumer’s copy, and a consumer portal.
- Disputes: a dispute endpoint; the record is re-checked with the registry or court that holds it, with an outcome inside 30 days, and a correction reaches every partner that received it.
- Example: A screening company furnishes a report an employer or landlord will act on; the order runs the consent, notice and dispute steps.
Under a written agreement: Request FCRA Partner Access.
The records can be the same; the process around them is not. How the program works: FCRA Partner Access · the orders endpoint: FCRA orders API · reference and statute citations: FCRA docs. This page describes the product, not legal advice.
Criminal record verification API pricing
The call, the live check and the artefacts are separate lines, each metered only when it completes.
| Item | Price | Unit |
|---|---|---|
| Live re-verification | +$0.02 | per completed live source, $2.00 ceiling per search; a source that did not answer is not billed |
| Criminal search (cached answer) | $0.15 | per call, the first 2,000 in a calendar month |
| Free to start | 25 calls | once per account, on production data, no card |
| Sandbox | $0 | 60 calls a minute and 2,000 a day per test key |
| FCRA order | $4.95 | per order; sex offender and criminal court search included, with no court fees or extra charges |
No minimum on search. The Map API has its own $300 monthly minimum. No seats, no platform fee, no per-state fee; a batch row is billed as one call.
Verification PDF of a search: $0.02 a document. Registry evidence document: $0.25 each, $5.00 ceiling a search. Sandbox searches, live blocks and reports are never billed.
Worked examples by volume: background check API pricing · every rate: pricing.
Where a verification API fits in a screening provider’s pipeline
Three places teams put the live block, and the one field each one reads.
Before furnishing a hit
A stored record matched a candidate. The pipeline re-sends the search with live.scope: "matched" and furnishes only records whose sources[].live is true. The field that decides: sourceStatus[].ok.
Refreshing an old hit
A record first seen a year ago comes up again. Rather than trusting lastCheckedAt, the provider names the jurisdiction in live.jurisdictions and reads lifecycle.droppedOut to learn whether the jurisdiction still lists it.
A date of birth the record lacks
A county publishes no birth dates. The live check asks the county whether it holds the record under the date you sent; dobVerification: source_confirmed lifts it to dob_match, source_not_confirmed keeps it ranked below.
Whole rosters start from the workforce screening API; an open-warrant question from the warrant search API; both share the key and record object of the background check API hub.
Limits of a criminal record verification API
Rate limits, the response cap, what a 429 looks like, and what the API does not return.
| Limit | Value |
|---|---|
| Sandbox, per test key | 60 calls a minute, 2,000 a day; nothing billed |
| Production, per key | No published ceiling. A 429 only when the service is at capacity, with Retry-After; a 429 is never billed |
| Records per response | Up to 4,000 in one answer; above that capped: true, and perPage pages through the rest |
| Batch | 1,000 rows a call; a failing row is reported in place and never stops the batch |
| Deadline | deadlineMs bounds the wait for slow jurisdictions; an unanswered one is named in sourceStatus and the answer says partial |
| Endpoints | POST /v1/criminal/search; one X-API-Key header |
- No civil cases, no traffic citations. Criminal records only: jail bookings, prison custody, court cases with dispositions, warrants, and the registries.
- No international records. US county, state and federal sources, and the 58 US registries.
- Not a consumer report. A hiring, housing, credit or insurance decision runs as an order through FCRA Partner Access, not through the standard key.
- No SSN, no credit, no verifications, no drug or driving records. Scope comes from the name, the date of birth and the places you already hold.
- No verification of a record the jurisdiction does not publish. A live check asks a system about what it lists; it cannot confirm a sealed or unpublished case.
- Live checks are not offered inside a batch.
POST /v1/batchreads the stored answer; send the rows that need confirming one at a time with aliveblock.
Retry rules and backoff: rate limits, retries and idempotency · every status code: errors.
HTTP/1.1 429 Too Many Requests Retry-After: 12 Content-Type: application/json { "detail": { "code": "overloaded", "retryAfterSeconds": 12 } }Nothing was searched and nothing was billed; wait
retryAfterSeconds and send the same request again.Criminal record verification API questions
Billing, scopes, status codes, evidence and disputes: the questions a CRA’s engineering team asks before a pilot.
What does "completed source" mean on the invoice?
A live source is billed only when the jurisdiction answered inside your request. One that timed out, refused or returned an error is named in sourceStatus with incomplete: true and costs nothing. The charge is $0.02 per completed source with a $2.00 ceiling on one search, on top of the $0.15 call.
Can I re-verify only the jurisdictions where the cached search found something?
Yes. Send live: { "scope": "matched" } instead of a jurisdictions list and only the systems holding a matching record are re-checked. If the cached answer found nothing, the API returns 422 no_live_sources rather than billing an empty live pass.
Why did my live search come back 202 instead of 200?
Up to two systems that can answer inside a request run inline and return 200. More systems, or one that cannot answer in-request, run as a job: 202 with a searchId, polled at GET /v1/criminal/searches/{searchId}. Most jurisdictions are in the second group, so build for 202.
How long can I make the verification PDF after a search?
For 7 days. POST /v1/criminal/report takes the searchId and returns a document that lists the criteria, each matching record and a source citation per record, billed at $0.02. Because the search was already billed, only the PDF is metered.
Who can check a signed receipt, and what does it reveal?
Anyone with the report ID, without a key: GET /v1/fcra/receipts/{orderId} is public, and the verify page runs the same check in a browser. The receipt holds the order id, the completion time, the control version and the report and PDF fingerprints. It contains no name, date of birth or record.
What happens to a record a consumer disputes?
On an FCRA order, POST /v1/fcra/orders/{id}/disputes opens the dispute. The record is re-checked with the court or agency that holds it, the outcome arrives within 30 days, and a record that is modified or deleted produces a revised report with its own receipt. Any adverse action on the order pauses while the dispute is open.
Is a verification through the standard key a consumer report?
No. The response carries a legal.notice saying so. A record that will be furnished for a hiring, housing, credit or insurance decision runs as an order through FCRA Partner Access at $4.95, which runs the live re-verification, the evidence and the receipt as part of the order.
Learn the API by example
Runnable guides that use this endpoint as the worked example.
All guides: Learn APIs by example · reference: developer docs · packages: SDKs and CLI.
Related background check API pages
The rest of the developer surface, one anchor per page.
Verify your first criminal record against the sandbox
Mint an os_test_ key, send the felony scenario with a live block, and read usage.liveChecks. Nothing is billed.
The OffenderSearch APIs
One account, one key, one bill.