# Live evidence — the registry’s page as a PDF

> Ask for live evidence and the search prints the registry’s own website — result pages and detail pages — to one PDF per registry, with a cover page and hash.

- **HTML:** https://offendersearch.app/docs/live-evidence
- **Base URL:** https://api.offendersearch.app
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **All documentation as markdown:** https://offendersearch.app/docs.md

## 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](https://offendersearch.app/docs/freshness.md) 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](https://offendersearch.app/docs/clearances.md) 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.

```bash
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.

```json
{
  "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](https://offendersearch.app/docs/async-and-webhooks.md) 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.

```json
{
  "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**.

```bash
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

| Limit | Value |
| --- | --- |
| 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

`POST /v1/evidence/searches` · auth: `X-API-Key`

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](https://offendersearch.app/docs/search#reference-post-v1-search). 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.

| Field | Type | Default | 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"`. |

**Errors**

| 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[]

`GET /v1/evidence/searches/{searchId}` · auth: `X-API-Key`

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**

| Field | Type | Required / default | Description |
| --- | --- | --- | --- |
| `searchId` | `string` | required | The `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](https://offendersearch.app/docs/search#reference-post-v1-search), plus:

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `evidence` | array of [EvidenceDocument](https://offendersearch.app/docs/live-evidence#object-evidencedocument) |  | One EvidenceDocument per registry named in `live.jurisdictions`, complete or incomplete. `[]` while the job is `pending` or `running`. |

**Errors**

| 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](https://offendersearch.app/docs/errors#object-error). |

## Download a live evidence document (PDF)

`GET /v1/evidence/searches/{searchId}/{jurisdiction}.pdf` · auth: `X-API-Key or session`

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**

| Field | Type | Required / default | Description |
| --- | --- | --- | --- |
| `searchId` | `string` | required | The `searchId` of a live evidence search on this account. Example `"srch_7d41e2"`. |
| `jurisdiction` | `string` | required | The `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**

| 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](https://offendersearch.app/docs/errors#object-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}`](https://offendersearch.app/docs/live-evidence#reference-get-v1-evidence-searches-searchid).

| Field | Type | Default | 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](https://offendersearch.app/docs/freshness#object-liveusage), plus:

| Field | Type | Default | 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`. |

---

## Related

- Previous: [Regular & Live modes, and source coverage](https://offendersearch.app/docs/freshness.md)
- Next: [Jurisdictions & codes](https://offendersearch.app/docs/jurisdictions.md)
- Index: [OffenderSearch API documentation](https://offendersearch.app/docs.md)
