OffenderSearch
API Reference · v1.0.0

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.app

Who 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 checkLive evidence search
Hostapi.offendersearch.appevidence.offendersearch.app — same key, same header
How you askPOST /v1/search with a live blockPOST /v1/evidence/searches with the identical body
What comes backRecords in records[], each with personRefThe same records, plus one entry per named registry in evidence[] with a PDF
Speed≤2 registries may run inline; more → asynchronousAlways asynchronous — printing takes 10 to 90 seconds per registry
PriceThe call plus $0.02 per completed registry, $2.00 ceilingThe 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.

Start a live evidence search
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.

The acknowledgement
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.

The polled result, with evidence[]
{
  "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, pages split into resultPages and detailPages, capturedAt in UTC, sourceUrl and every pageUrls entry, the sha256 of the PDF, its bytes, the downloadUrl, and the charge.
  • The PDF itself is in pdfBase64 when the document is 4 MB or smaller (inline: true). Above that, pdfBase64 is null, inline is false, and the download link is the way to get it.
  • An incomplete entry carries incompleteReason from a closed list — no_recipe, renderer_unavailable, source_unreachable, blocked, timed_out, no_results_page, render_failed or query_unsupported — a free-text detail, and billed: false. Branch on the reason, never on detail.

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.

Download the document
curl https://evidence.offendersearch.app/v1/evidence/searches/srch_7d41e2/CA.pdf \
  -H "X-API-Key: $OS_API_KEY" \
  -o jordan-rivera-CA.pdf

What 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.

Some registries cannot be printed yet. The list grows. A registry that cannot be printed answers 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.liveEvidence is the count of billed documents and usage.liveEvidenceUsd the charge, beside the usual liveChecks and liveChargeUsd; each entry in evidence[] carries its own billed and chargeUsd.

Limits

Registries per requestUp to 20 in live.jurisdictions. Name the registries that matter.
Pages per registryUp to 25, result pages and detail pages together.
Retention30 days from capturedAt; after that the download link answers 404.
Inline cap4 MB. Larger documents come only by the download link (inline: false).
Time10 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.

EndpointRole
POST /v1/evidence/searchesStart 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}.pdfThe document itself, as application/pdf.
POST/v1/evidence/searchesAuth: X-API-Key

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

Body · application/json

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

202 · application/json

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.

FieldTypeDescription
searchId
stringThe 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
stringPath of GET /v1/evidence/searches/{searchId} on the evidence host.
Example "/v1/evidence/searches/srch_7d41e2"
Errors
StatusCodeWhen
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.
GET/v1/evidence/searches/{searchId}Auth: X-API-Key

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

Path parameters
FieldTypeDescription
searchId
required
stringThe searchId from the 202 of POST /v1/evidence/searches.
Example "srch_7d41e2"

Response

200 · application/json

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:

FieldTypeDescription
evidence
array of EvidenceDocumentOne EvidenceDocument per registry named in live.jurisdictions, complete or incomplete. [] while the job is pending or running.
Errors
StatusCodeWhen
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.
GET/v1/evidence/searches/{searchId}/{jurisdiction}.pdfAuth: X-API-Key or session

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

Path parameters
FieldTypeDescription
searchId
required
stringThe searchId of a live evidence search on this account.
Example "srch_7d41e2"
jurisdiction
required
stringThe jurisdiction of the evidence entry, as returned in evidence[].
Example "CA"

Response

200 · application/pdf

The document: our cover page, then the registry's pages as shown.

Errors
StatusCodeWhen
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}.

FieldTypeDescription
jurisdiction
stringThe 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 nullPages of registry content in the PDF, after the cover page. Present only when status is complete.
Example 3
resultPages
integer or nullHow many of pages are result pages. Present only when status is complete.
Example 2
detailPages
integer or nullHow many of pages are registrant detail pages. Present only when status is complete.
Example 1
capturedAt
string (date-time) or nullWhen 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 nullThe registry website the search was run on. Present only when status is complete.
Example "https://…"
pageUrls
array of string or nullThe URL of every page captured, in document order. Present only when status is complete.
sha256
string or nullSHA-256 of the PDF, in hex; the same value is printed on the cover page. Present only when status is complete.
bytes
integer or nullSize of the PDF in bytes. Present only when status is complete.
Example 412330
downloadUrl
string or nullPath 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
booleantrue when this document was billed ($0.25). Always false on an incomplete document.
chargeUsd
number or nullThe charge for this document in US dollars. Present only when status is complete.
Example 0.25
pdfBase64
string or nullThe 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 nulltrue 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 nullA 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:

FieldTypeDescription
liveEvidence
integerLive evidence documents that completed and were billed on this search.
Example 1
liveEvidenceUsd
numberThe 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