# Search - POST /v1/criminal/search

> The primary endpoint, in two modes: cached by default, live verification when you send a live block. Full parameter reference, sync vs async, worked examples.

- **HTML:** https://offendersearch.app/docs/criminal/search
- **Base URL:** https://api.offendersearch.app
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **Criminal reference as markdown:** https://offendersearch.app/docs/criminal.md

## Two modes, decided by the request body

There is one search endpoint. Omit `live` for the **cached** answer — one indexed query across the continuously-updated corpus, sub-second, no live source consulted. Send a `live` block for **live verification** — the actual jurisdiction(s) are re-checked in-request and merged with the cached answer through the same matcher.

| Mode | How you ask | What it does | Billing |
| --- | --- | --- | --- |
| Cached *(default)* | omit `live` | One indexed query across the whole corpus. No live source is ever consulted. | one `criminal_call` |
| Live verification | send a `live` block | Re-checks the actual jurisdiction(s) in-request, then merges with the cached answer. | `criminal_call` + `criminal_live_source` per completed source, to a $2.00 ceiling |

## POST /v1/criminal/search

**Authentication:** `X-API-Key` header. No entitlement is required — Criminal Search and the `live` mode are available on every account automatically.

### Request — top level

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `query` | object | required | The search predicate. `lastName` is required inside it. |
| `live` | object | optional | Presence switches on live verification: `{ "jurisdictions": [...] }`, `{ "scope": "matched" }`, and/or `{ "websites": [...] }`. |
| `include` | string[] | optional | `["sourceData"]` un-omits the per-source annex; the response reports what it left out in `omittedFields`. |
| `page` | integer | optional · default `1` | Top level, not inside `query`. `>= 1`. |
| `perPage` | integer | optional · default `50` | Top level. Clamped to 1–200. |

### Request — the query object

| Field | Type | Notes |
| --- | --- | --- |
| `lastName` | string | **Required.** Also accepted as `last`. Missing / empty → `422 guard_unbounded_query`. |
| `firstName` | string | Also accepted as `first`. When absent, the first name is not considered (`firstName:notQueried`). |
| `dob` | string `YYYY-MM-DD` | A full date. Year and month are derived from it automatically. |
| `birthYear` | integer | A year on its own. |
| `birthMonth` | integer | `1–12`. Refines a year match; ignored without a year. |
| `age` | integer | Used against age-only records; ±1-year tolerance. Consulted only when `dob` cannot resolve. |
| `state` | string | USPS code. A location **filter**, not a registry selector. |
| `county` | string | County filter, case-insensitive. Also accepted as `jurisdiction`. |
| `website` | string | A roster/records URL that **scopes** the cached search to the jurisdiction it resolves to. |
| `nameStrategy` | string | `prefix` (default) or `exact`. Selects the surname strategy. |
| `prefixMatch` | string \\| string[] | Requests explicit prefix matching on a named field. Below 3 chars → `422 prefix_too_short`. |

> **state** and **county** filter the jurisdiction; **city**, **zipcode** and **address** filter the residential address where the source publishes one (registry/wanted records). No lat/lng radius yet.

## Live verification — sync vs async

A live check of **≤2 sync-eligible sources** runs synchronously (`200`, inline). More sources, or any source that needs heavier handling, fans out to an **asynchronous** job (`202` + poll). Because one public county code can expand to several internal systems, a single code may resolve to more than two and run async — `liveSourcesRequested` in the `202` body reports the true count.

```bash
curl -X POST "https://api.offendersearch.app/v1/criminal/search" \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" -H "Content-Type: application/json" \
  -d '{ "query": { "lastName": "Hamilton", "state": "TX", "dob": "1989-04-23" },
        "live": { "jurisdictions": ["TX-DALLAS"] } }'
```

On a live search, read `counts.sourcesIncomplete` first: when it is `> 0`, an empty result is a lower bound, not a confirmed absence, and `warnings[]` names the sources that fell short.

## The response envelope

Every top-level key is always present — test the value, never for key existence. On a cached search you get every key except `usage` and `resolution` (`sourceStatus` is `[]`). On a live search you additionally get `usage`, a populated `sourceStatus`, and `resolution` if a website drove the request.

```json
{
  "status": "complete",
  "counts": { "records": 1, "recordsReturned": 1,
              "sourcesQueried": 1, "sourcesComplete": 1, "sourcesIncomplete": 0 },
  "warnings": [],
  "page": 1, "perPage": 50, "totalPages": 1,
  "sourceStatus": [
    { "code": "TX-DALLAS", "source": "TX-DALLAS", "ok": true, "incomplete": false,
      "incompleteReason": null, "records": 1,
      "lastCheckedAt": "2026-08-25T15:04:11+00:00", "live": true }
  ],
  "omittedFields": ["sourceData"],
  "records": [
    { "externalId": "TX-DALLAS:booking:889201",
      "name": { "first": "ALEX", "middle": "Q", "last": "HAMILTON", "suffix": "" },
      "matchConfidence": 0.95, "matchBasis": "lastName:exact firstName:notQueried dob:match",
      "matchState": "dob_match", "unverified": false, "liveChecked": true }
  ],
  "usage": { "liveChecks": 1, "liveChargeUsd": 0.02, "capped": false,
             "display": "1 live checks · $0.02" },
  "searchId": "crs_7f2a1c9e0b4d6a8f1e23",
  "legal": { "notice": "Not a consumer report. This information may not be used for any purpose under the Fair Credit Reporting Act (15 U.S.C. § 1681 et seq.)." }
}
```

---

## Related

- Previous: [Errors & status codes](https://offendersearch.app/docs/criminal/errors.md)
- Next: [Matching, confidence & partial names](https://offendersearch.app/docs/criminal/matching.md)
- Index: [Criminal Search API reference](https://offendersearch.app/docs/criminal.md)
