# Sex offender API reference

> Every sex offender API endpoint with its request and response fields, and every object it returns — Record, SourceRef, SourceStatus — each defined once.

- **HTML:** https://offendersearch.app/docs/api-reference
- **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

## How the sex offender 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. Shared objects — the Record a search returns, the per-registry SourceStatus, the Error body — are defined once and linked from every table that carries them.

## Sex offender API endpoints — request and response fields

Each endpoint links to its reference block: a Request table (headers, path, query and body fields, with type, required and default) and a Response table (every field, with type and when it is null).

| Endpoint | What it does |
| --- | --- |
| [`POST /v1/search`](https://offendersearch.app/docs/search#reference-post-v1-search) | **Search.** Search every US sex offender registry in one call. Send a SearchRequest; get back one de-duplicated, scored result set with a status for every registry searched. |
| [`POST /v1/searches`](https://offendersearch.app/docs/async-and-webhooks#reference-post-v1-searches) | **Async search.** Submit a search and collect the result later, by polling or by webhook. Accepts every SearchRequest field plus `webhookUrl`, including a `live` block (the named registries are re-verified live inside the job and billed as on `POST /v1/search`), and returns `202` with a job id immediately. |
| [`GET /v1/searches/{searchId}`](https://offendersearch.app/docs/async-and-webhooks#reference-get-v1-searches-searchid) | **Get a search.** Read an async job's status and, once it has finished, its full result. |
| [`GET /v1/searches/{searchId}/results`](https://offendersearch.app/docs/async-and-webhooks#reference-get-v1-searches-searchid-results) | **Stored search results.** Re-open a past search on this account and get back exactly the records it returned. The search is not run again and the call is not billed. |
| [`POST /v1/evidence/searches`](https://offendersearch.app/docs/live-evidence#reference-post-v1-evidence-searches) | **Start a live evidence search.** 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}`. |
| [`GET /v1/evidence/searches/{searchId}`](https://offendersearch.app/docs/live-evidence#reference-get-v1-evidence-searches-searchid) | **Poll a live evidence search.** 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`. |
| [`GET /v1/evidence/searches/{searchId}/{jurisdiction}.pdf`](https://offendersearch.app/docs/live-evidence#reference-get-v1-evidence-searches-searchid-jurisdiction-pdf) | **Download an evidence document.** 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. |
| [`POST /v1/batch`](https://offendersearch.app/docs/batch#reference-post-v1-batch) | **Batch search.** Run up to 1000 searches in one call, one result per input row, in input order. Each row is billed as one search. |
| [`POST /v1/report`](https://offendersearch.app/docs/reports#reference-post-v1-report) | **Verification report.** One consolidated PDF for a search you already ran, with a source citation on every record. The search's recorded criteria are run again when you request it. |
| [`POST /v1/searches/{searchId}/proof`](https://offendersearch.app/docs/reports#reference-post-v1-searches-searchid-proof) | **Proof documents.** Render per-registry proof documents for a search you already ran. Needs billing on the account. |
| [`GET /v1/proof-docs/{token}`](https://offendersearch.app/docs/reports#reference-get-v1-proof-docs-token) | **Fetch a proof document.** Download one rendered proof document by the token in its `documents[].url`. |
| [`GET /v1/records/{recordId}`](https://offendersearch.app/docs/record-object#reference-get-v1-records-recordid) | **Get a record.** Fetch one Record by the `recordId` or the registry `uuid` a search returned. A `recordId` resolves while the search that returned it is retained (14 days). |
| [`GET /v1/sources`](https://offendersearch.app/docs/freshness#reference-get-v1-sources) | **List sources.** The coverage catalog: every registry the API searches, by code, with its coverage and health. No API key required. |
| [`POST /v1/compat/sexoffender`](https://offendersearch.app/docs/migration#reference-post-v1-compat-sexoffender) | **Compatibility search.** The drop-in endpoint: legacy parameter names in, the legacy `{offenders, page, totalPages}` envelope out. Switch an existing integration by changing the base URL and key. |
| [`GET /v1/compat/sexoffender`](https://offendersearch.app/docs/migration#reference-get-v1-compat-sexoffender) | **Compatibility search (GET).** The same search as the POST form, with the parameters in the query string. |
| [`POST /v1/clearances`](https://offendersearch.app/docs/clearances#reference-post-v1-clearances) | **Record a clearance.** Record that the person behind a `personRef` is (`this_person`) or is not (`not_this_person`) the subject identified by your `subjectId`. The verdict must cite the search that showed the person. A second verdict for the same subject, client and person replaces the first. Included in the search price; nothing here is metered. |
| [`GET /v1/clearances`](https://offendersearch.app/docs/clearances#reference-get-v1-clearances) | **List a subject's clearances.** Every clearance recorded for one `subjectId` (and `clientId`, when you send one), newest first. Active clearances only unless `includeRevoked=true`. The ids you send are hashed before the lookup, so a listing needs the same `subjectId` and `clientId` the verdicts were recorded under. |
| [`GET /v1/clearances/{clearanceId}`](https://offendersearch.app/docs/clearances#reference-get-v1-clearances-clearanceid) | **Retrieve a clearance.** One clearance by id — the `clearanceId` a search response's `cleared[]` or `clearance` carries. `subjectId` and `clientId` are `null` on this route: only a per-account hash of them is stored. |
| [`DELETE /v1/clearances/{clearanceId}`](https://offendersearch.app/docs/clearances#reference-delete-v1-clearances-clearanceid) | **Revoke a clearance.** Sets `revokedAt`. The verdict stops applying at once: the next search for that subject shows the person again. The clearance stays readable with `includeRevoked=true`, so the audit trail is not lost. |

## Sex offender API objects — Record, SourceRef, 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.

| Object | What it is |
| --- | --- |
| [`Query`](https://offendersearch.app/docs/search#object-query) | Who or where to search for. Every field is optional; send what you know. A name, a date of birth or age, or a location narrows the search, and a query that narrows on nothing is refused with a `422` rather than answered with a partial list. |
| [`LiveBlock`](https://offendersearch.app/docs/freshness#object-liveblock) | Asks for named registries to be re-checked at query time (Live verification). Omit it for the Regular answer. Send `jurisdictions` or `scope`, not both. |
| [`SourceStatus`](https://offendersearch.app/docs/result-completeness#object-sourcestatus) | One registry's outcome within a search. Each registry the search touched has one entry in `sourceStatus[]`, so coverage is stated per response rather than assumed. |
| [`LiveUsage`](https://offendersearch.app/docs/freshness#object-liveusage) | The live-check charge summary. Present only on the result of a search that carried a `live` block — the inline response, or the polled result of a live job; its presence is the signal that the answer was re-verified live. |
| [`MeterEvent`](https://offendersearch.app/docs/freshness#object-meterevent) | One billable unit behind a live result. Present only on a live result, in `meterEvents[]`. |
| [`EvidenceLiveUsage`](https://offendersearch.app/docs/live-evidence#object-evidenceliveusage) | 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. |
| [`EvidenceDocument`](https://offendersearch.app/docs/live-evidence#object-evidencedocument) | 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. |
| [`ProofRequest`](https://offendersearch.app/docs/reports#object-proofrequest) | Asks for per-registry proof documents: the registries to render and the format. Billing must be enabled on the account. Each rendered document is billed as one PDF, at the same rate as a verification report. |
| [`ProofBundle`](https://offendersearch.app/docs/reports#object-proofbundle) | The proof documents rendered for a search. `{"status": "none"}` when no proof was requested. |
| [`Record`](https://offendersearch.app/docs/record-object#record-fields) | One registrant, normalized to the same shape across every US registry. Every key is present on every record; an empty value is `""`, `[]` or `null` as stated per field, never a missing key. |
| [`Name`](https://offendersearch.app/docs/record-object#object-name) | A person's name as the registry publishes it, split into parts. Each part is `""` when the registry does not publish it. |
| [`Address`](https://offendersearch.app/docs/record-object#object-address) | One published address. A record carries every address the registry publishes, each typed. |
| [`Offense`](https://offendersearch.app/docs/record-object#object-offense) | One offense as the registry publishes it. Every field is present; a field the registry does not publish is `""` (or `null` for `federal`). Dates are ISO-8601, at the precision the registry published. |
| [`SourceRef`](https://offendersearch.app/docs/record-object#object-sourceref) | Where a record came from: the registry, its own link to the registrant, and three timestamps. `source` is the registry that supplied the record's top-level values; `sources[]` lists every registry that holds the person. |
| [`StateData`](https://offendersearch.app/docs/record-object#object-statedata) | The extended per-state detail. Returned only when the request sends `include: ["stateData"]`; otherwise `stateData` is `null` and `omittedFields` names it. Each field is `""`, `[]` or `null` when the registry does not publish it. |
| [`Error`](https://offendersearch.app/docs/errors#object-error) | The error body. `detail` is a sentence you can log or show; the HTTP status is the value to branch on. |
| [`RateLimitError`](https://offendersearch.app/docs/errors#object-ratelimiterror) | The `429 Too Many Requests` body. The `Retry-After` header carries the same wait as `detail.retryAfterSeconds`. |
| [`OffendersIoRequest`](https://offendersearch.app/docs/migration#object-offendersiorequest) | The parameters of the compatibility endpoint, in the legacy offenders.io names. Sent as a JSON body to `POST /v1/compat/sexoffender`, or as query parameters to `GET /v1/compat/sexoffender`. |
| [`OffendersIoResponse`](https://offendersearch.app/docs/migration#object-offendersioresponse) | The legacy `{offenders, page, totalPages}` envelope, returned unchanged so an existing integration keeps working. |
| [`OffendersIoOffender`](https://offendersearch.app/docs/migration#object-offendersiooffender) | One registrant in the legacy flat shape: every legacy key, plus `personUuid`, `county`, `stateData`, `matchConfidence` and `matchState`. Coordinates use `-1` for missing, not `null`. |
| [`CompatError`](https://offendersearch.app/docs/migration#object-compaterror) | The error body on the compatibility endpoint: `{code, message}` in the legacy shape, instead of `{detail}`. |
| [`Clearance`](https://offendersearch.app/docs/clearances#object-clearance) | One verdict: the person behind a `personRef` is, or is not, the subject being screened. Returned by every `/v1/clearances` route. `subjectId` and `clientId` are echoed only on the request that carried them (create and list); on `GET /v1/clearances/{clearanceId}` they are `null`, because only a per-account hash of them is stored. |
| [`ClearedEntry`](https://offendersearch.app/docs/clearances#object-clearedentry) | One 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. |
| [`RecordClearance`](https://offendersearch.app/docs/clearances#object-recordclearance) | The 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. |

---

## Related

- Previous: [Batch & CSV search](https://offendersearch.app/docs/batch.md)
- Next: [The Record object](https://offendersearch.app/docs/record-object.md)
- Index: [OffenderSearch API documentation](https://offendersearch.app/docs.md)
