Live evidence — the registry’s page as a PDF
The registry’s own website, printed to one PDF per registry with a cover page, a content hash and a 30-day download link.
Base URL https://api.offendersearch.appWho live evidence is for
Some files have to hold what the registry itself showed, not only a record of it. A skilled nursing admission that must survive a survey, a visitor-management log with an audit obligation, a home care or staffing agency that keeps what the registry displayed on the day of the check, a legal or licensing file — in each of those the question later is “what did the registry’s page say when you looked?”, and a database row does not answer it.
Live evidence answers it. It is a separate service on its own base URL, https://evidence.offendersearch.app, used with the same API key and X-API-Key header as the main API. Send it the same request body you send POST /v1/search: it runs the live check for the registries you name, and in addition opens each registry’s own public website in a real browser, runs the search there, and prints every result page and each matching registrant’s detail page to one PDF per registry, behind a cover page that states the query, the registry, the capture time, the URLs captured and a content hash. Most callers do not need it; the main API is unchanged. This is the path for compliance-heavy flows where a record of the registry’s own page is required.
Records or documents — a live check and a live evidence search
A live check on the main API re-verifies a registry and gives you back records: normalized, de-duplicated, scored, each with a personRef. A live evidence search gives you the same records and documents: the registry’s own pages, printed as shown. You name the registries the same way in both — live.jurisdictions — and the records arrive exactly as the main API returns them, because they come from the normal live check; the documents are added beside them.
| Live check | Live evidence search | |
|---|---|---|
| Host | api.offendersearch.app | evidence.offendersearch.app — same key, same header |
| How you ask | POST /v1/search with a live block | POST /v1/evidence/searches with the identical body |
| What comes back | Records in records[], each with personRef | The same records, plus one entry per named registry in evidence[] with a PDF |
| Speed | ≤2 registries may run inline; more → asynchronous | Always asynchronous — printing takes 10 to 90 seconds per registry |
| Price | The call plus $0.02 per completed registry, $2.00 ceiling | The same, plus $0.25 per completed document, $5.00 ceiling on the documents |
Clearances apply to the records as usual. A cleared person’s page is still printed: the page is the registry’s statement, and the clearance is your decision about it, so the document stays faithful to what the registry showed.
The flow: request, poll, download
Post the body you would send POST /v1/search to POST /v1/evidence/searches on the evidence host. The registries named in live.jurisdictions are the ones that get a printed document — up to 20 per request.
curl https://evidence.offendersearch.app/v1/evidence/searches \
-H "X-API-Key: $OS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "firstName": "Jordan", "lastName": "Rivera", "dob": "1988-04-12" },
"live": { "jurisdictions": ["CA", "NV"] }
}'A live evidence search always runs asynchronously, even for one registry: the answer is a 202 with a job handle, never an inline result.
HTTP/1.1 202 Accepted
{
"searchId": "srch_7d41e2",
"status": "pending",
"mode": "async",
"pollUrl": "/v1/evidence/searches/srch_7d41e2"
}Poll GET /v1/evidence/searches/{searchId} on the evidence host until status leaves pending and running — the same handshake as an async search on the main API. The finished result is the full search response exactly as POST /v1/search returns it, with one addition: evidence[], one entry per registry you named.
{
"searchId": "srch_7d41e2",
"status": "complete",
"counts": { "records": 1, "recordsReturned": 1, "sourcesQueried": 2,
"sourcesComplete": 2, "sourcesIncomplete": 0 },
"sourceStatus": [ /* one entry per registry, as on every search */ ],
"records": [ /* one Record per matched registrant, with personRef, as on every search */ ],
"evidence": [
{
"jurisdiction": "CA",
"status": "complete",
"kind": "text",
"pages": 3,
"resultPages": 2,
"detailPages": 1,
"capturedAt": "2026-10-18T14:02:11+00:00",
"sourceUrl": "https://…",
"pageUrls": ["https://…", "https://…", "https://…"],
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"bytes": 412330,
"downloadUrl": "/v1/evidence/searches/srch_7d41e2/CA.pdf",
"billed": true,
"chargeUsd": 0.25,
"pdfBase64": "JVBERi0x…",
"inline": true
},
{
"jurisdiction": "NV",
"status": "incomplete",
"incompleteReason": "no_recipe",
"detail": "This registry cannot be printed yet.",
"billed": false
}
],
"usage": {
"liveChecks": 2, "liveChargeUsd": 0.04,
"liveEvidence": 1, "liveEvidenceUsd": 0.25,
"capped": false, "display": "2 live checks · 1 document · $0.29"
}
}- A complete entry carries the capture:
kind,pagessplit intoresultPagesanddetailPages,capturedAtin UTC,sourceUrland everypageUrlsentry, thesha256of the PDF, itsbytes, thedownloadUrl, and the charge. - The PDF itself is in
pdfBase64when the document is 4 MB or smaller (inline: true). Above that,pdfBase64isnull,inlineisfalse, and the download link is the way to get it. - An incomplete entry carries
incompleteReasonfrom a closed list —no_recipe,renderer_unavailable,source_unreachable,blocked,timed_out,no_results_page,render_failedorquery_unsupported— a free-textdetail, andbilled: false. Branch on the reason, never ondetail.
Download any completed document with the API key or a dashboard session. The search must belong to your account; any other search id answers 404. Documents are kept for 30 days.
curl https://evidence.offendersearch.app/v1/evidence/searches/srch_7d41e2/CA.pdf \
-H "X-API-Key: $OS_API_KEY" \
-o jordan-rivera-CA.pdfWhat a document contains
Every PDF has the same two parts, in this order:
- Our cover page. The query as sent, the registry printed, the capture time in UTC, the URL of every page captured, the page count, and the SHA-256 of the document that follows — so a reviewer can tell, years later, that the file is intact and which registry and moment it describes.
- The registry’s pages, as shown. Every result page the search produced on the registry’s website, then the detail page of each registrant that matched the query. Nothing is re-typed or re-formatted; the page is what the registry displayed at that moment.
kind tells you how the registry pages were produced. text means they were printed from the page and the PDF is searchable. image means each page is a full-page capture rendered as an image — some registries only work this way, and the result is a faithful picture rather than selectable text. mixed means a document holds both.
incompleteReason: "no_recipe", is never billed as a document, and its live check still runs and returns records as usual.Price, and what is never billed
- The search is billed exactly like a live search on the main API. The call at the base per-call rate, plus $0.02 per live source that completes, to the usual $2.00 ceiling.
- Plus $0.25 per registry document that completes. The evidence charge for one search never exceeds $5.00 — a price ceiling, not a document limit.
- Incomplete documents bill nothing. Whatever the reason, an incomplete entry reports
billed: false; a registry that cannot be printed yet is never billed as a document. - Read it off the response.
usage.liveEvidenceis the count of billed documents andusage.liveEvidenceUsdthe charge, beside the usualliveChecksandliveChargeUsd; each entry inevidence[]carries its ownbilledandchargeUsd.
Limits
| Registries per request | Up to 20 in live.jurisdictions. Name the registries that matter. |
| Pages per registry | Up to 25, result pages and detail pages together. |
| Retention | 30 days from capturedAt; after that the download link answers 404. |
| Inline cap | 4 MB. Larger documents come only by the download link (inline: false). |
| Time | 10 to 90 seconds per registry; always asynchronous. |
Endpoints
All three live on https://evidence.offendersearch.app, not on the main API’s base URL. The main API’s endpoints are unchanged.
| Endpoint | Role |
|---|---|
POST /v1/evidence/searches | Start the search. Same body as POST /v1/search; always 202. |
GET /v1/evidence/searches/{searchId} | The poll. The finished result is the full search response plus evidence[]. |
GET /v1/evidence/searches/{searchId}/{jurisdiction}.pdf | The document itself, as application/pdf. |
Live evidence search API: start a search that prints the registry's page
On the Live Evidence host — https://evidence.offendersearch.app, not the main API's base URL; same key, same header. Send the same body as POST /v1/search; the registries named in live.jurisdictions (up to 20) are the ones printed. The live check runs as usual, so the records come back exactly as POST /v1/search returns them, and each named registry's own public website is printed to one PDF behind a cover page. Always answers 202; collect the result from GET /v1/evidence/searches/{searchId}.
Request
Identical to the body of POST /v1/search. live.jurisdictions names the registries to print.
The body is the SearchRequest object. The body of POST /v1/search: the person or place to search for, and how widely and how strictly to match.
Response
The job was accepted. Poll pollUrl on the evidence host. The search is billed like a live search — the call plus $0.02 per completed live source — and each registry document that completes adds $0.25, to a $5.00 ceiling per search.
| Field | Type | Description |
|---|---|---|
| searchId | string | The job's id. Keep it: it is also the id the documents are downloaded under. Example "srch_7d41e2" |
| status | string one of "pending" | Always pending on acceptance. |
| mode | string one of "async" | Always async: a live evidence search never answers inline. |
| pollUrl | string | Path of GET /v1/evidence/searches/{searchId} on the evidence host.Example "/v1/evidence/searches/srch_7d41e2" |
| Status | Code | When |
|---|---|---|
| 401 | — | The X-API-Key header is missing or the key is not valid, and no valid dashboard session was sent. |
| 402 | — | The account has no active subscription and no free-trial searches left. detail says which. |
| 422 | — | A field is malformed or unrecognised, or live.jurisdictions is missing, empty or names more than 20 registries. detail says which. |
Poll a live evidence search: status, records and evidence[]
On the Live Evidence host — https://evidence.offendersearch.app. Read the job's status and, once it has finished, the full search response plus evidence[], one entry per registry named in live.jurisdictions.
Request
| Field | Type | Description |
|---|---|---|
| searchId required | string | The searchId from the 202 of POST /v1/evidence/searches.Example "srch_7d41e2" |
Response
When the job has finished (complete, or partial when a registry's live check did not finish), the full search response with evidence[] and usage.liveEvidence / usage.liveEvidenceUsd. While it is pending or running, a short body: searchId, status, zeroed counts, sourceStatus: [], records: [], evidence: []. When the job failed: searchId, status: "error", error, records: [], evidence: [].
Every field of the SearchResponse object, plus:
| Field | Type | Description |
|---|---|---|
| evidence | array of EvidenceDocument | One EvidenceDocument per registry named in live.jurisdictions, complete or incomplete. [] while the job is pending or running. |
| Status | Code | When |
|---|---|---|
| 401 | — | The X-API-Key header is missing or the key is not valid, and no valid dashboard session was sent. |
| 404 | — | No live evidence search with this id on this account (a search run by another account answers the same 404). The body is an Error. |
Download a live evidence document (PDF)
On the Live Evidence host — https://evidence.offendersearch.app. Download one registry's live evidence document — the registry's own public website, printed as shown behind a cover page. The path is the downloadUrl of the matching evidence[] entry. Not billed: the document was billed when it was produced.
Request
| Field | Type | Description |
|---|---|---|
| searchId required | string | The searchId of a live evidence search on this account.Example "srch_7d41e2" |
| jurisdiction required | string | The jurisdiction of the evidence entry, as returned in evidence[].Example "CA" |
Response
The document: our cover page, then the registry's pages as shown.
| Status | Code | When |
|---|---|---|
| 401 | — | The X-API-Key header is missing or the key is not valid, and no valid dashboard session was sent. |
| 404 | — | No live evidence search with this id on this account, no completed document for this registry on that search, or the document is past its 30-day retention. The body is an Error. |
The EvidenceDocument object — one registry's printed page
One registry's live evidence document: the registry's own public website, printed as shown to a PDF behind a cover page. One entry per registry named in live.jurisdictions, in evidence[]. A complete document carries the capture and download fields; an incomplete one carries incompleteReason and detail and is never billed.
Returned or accepted by GET /v1/evidence/searches/{searchId}.
| Field | Type | Description |
|---|---|---|
| jurisdiction | string | The registry this document covers, as the two-letter code or registry id sent in live.jurisdictions.Example "CA" |
| status | string one of "complete", "incomplete" | complete: the PDF was produced and can be downloaded. incomplete: it was not; read incompleteReason. |
| kind | string or null one of "text", "image", "mixed" | How the registry pages were produced. text: printed from the page, searchable. image: each page is a full-page capture rendered as an image (some registries only work this way). mixed: both in one document. Present only when status is complete. |
| pages | integer or null | Pages of registry content in the PDF, after the cover page. Present only when status is complete.Example 3 |
| resultPages | integer or null | How many of pages are result pages. Present only when status is complete.Example 2 |
| detailPages | integer or null | How many of pages are registrant detail pages. Present only when status is complete.Example 1 |
| capturedAt | string (date-time) or null | When the registry's pages were captured, in UTC. Present only when status is complete.Example "2026-10-18T14:02:11+00:00" |
| sourceUrl | string or null | The registry website the search was run on. Present only when status is complete.Example "https://…" |
| pageUrls | array of string or null | The URL of every page captured, in document order. Present only when status is complete. |
| sha256 | string or null | SHA-256 of the PDF, in hex; the same value is printed on the cover page. Present only when status is complete. |
| bytes | integer or null | Size of the PDF in bytes. Present only when status is complete.Example 412330 |
| downloadUrl | string or null | Path of GET /v1/evidence/searches/{searchId}/{jurisdiction}.pdf on the evidence host for this document, valid for 30 days. Present only when status is complete.Example "/v1/evidence/searches/srch_7d41e2/CA.pdf" |
| billed | boolean | true when this document was billed ($0.25). Always false on an incomplete document. |
| chargeUsd | number or null | The charge for this document in US dollars. Present only when status is complete.Example 0.25 |
| pdfBase64 | string or null | The PDF, base64-encoded, when the document is 4 MB or smaller. null above that — use downloadUrl. Present only when status is complete. |
| inline | boolean or null | true when pdfBase64 carries the document; false when it is available only by downloadUrl. Present only when status is complete. |
| incompleteReason | string or null one of "no_recipe", "renderer_unavailable", "source_unreachable", "blocked", "timed_out", "no_results_page", "render_failed", "query_unsupported" | Why no document was produced; set exactly when status is incomplete. no_recipe: this registry cannot be printed yet. renderer_unavailable: the printing service was not available — retry. source_unreachable: the registry website did not answer — retry. blocked: the registry website refused the visit. timed_out: printing did not finish in time — retry. no_results_page: the registry website showed no results page for this query. render_failed: the pages were reached but could not be printed. query_unsupported: this registry's website cannot search on the fields the query supplied. |
| detail | string or null | A sentence for logs explaining the incomplete reason. Its wording changes; branch on incompleteReason, never on this. |
The EvidenceLiveUsage object — live evidence charge fields
The usage block of a live evidence search: every LiveUsage field — the live check is billed exactly as on the main API — plus the evidence documents.
Every field of the LiveUsage object, plus:
| Field | Type | Description |
|---|---|---|
| liveEvidence | integer | Live evidence documents that completed and were billed on this search. Example 1 |
| liveEvidenceUsd | number | The live evidence charge for this search, in US dollars: $0.25 per completed registry document, at most $5.00. Separate from liveChargeUsd.Example 0.25 |
Product overview
- Sex offender search API
- Sex offender registry API
- Offender search API
- Background check API
- Bulk sex offender search
- Background check API pricing
- Background check API integration
- Integrations
- Free background check API
- Pay-as-you-go background check API
- Background check API in Python
- Background check API in Node.js
- NSOPW API