# Background check API response explained, field by field

> One real sandbox response, read group by group: what each key in the envelope, the sourceStatus rows and the record means, which fields decide identity, and how the criminal response differs.

- **HTML:** https://offendersearch.app/docs/learn/background-check-api-response-explained
- **Base URL (live):** https://api.offendersearch.app — live keys (`os_live_…`)
- **Base URL (sandbox):** https://sandbox.offendersearch.app — test keys (`os_test_…`), never billed
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **Docs index for agents:** https://offendersearch.app/llms.txt

## The background check API response at a glance

This page reads one real response from top to bottom. It is the answer the [sandbox](/sandbox) gives for the test person **Exact Match** with `dob` `1980-01-01`: one registrant in Ohio, identified by a full date of birth, listed by one registry. The shape is exactly what the production host returns for the [background check API](/background-check-api); only the values are fixtures. The request that produces it is three lines in any language. Python and Node.js SDKs ship with the first registry release; see [SDKs](/sdk). Until then the raw HTTP calls below are the integration.

**cURL**
```bash
curl -X POST https://sandbox.offendersearch.app/v1/search \
  -H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": {"firstName": "Exact", "lastName": "Match", "dob": "1980-01-01"}}'
```

**Python**
```python
import os, requests

BASE = "https://sandbox.offendersearch.app"          # os_test_… keys only; nothing is billed
KEY = os.environ["OFFENDERSEARCH_TEST_KEY"]          # os_test_…

r = requests.post(
    f"{BASE}/v1/search",
    headers={"X-API-Key": KEY, "Content-Type": "application/json"},
    json={"query": {"firstName": "Exact", "lastName": "Match", "dob": "1980-01-01"}},
    timeout=60,
)
r.raise_for_status()
data = r.json()
print(data["searchId"], data["status"], data["counts"])       # srch_sbx_so_exact complete {...}
for rec in data["records"]:
    print(rec["recordId"], rec["matchState"], rec["registrationState"], rec["source"]["recordUrl"])
```

**Node**
```javascript
const BASE = "https://sandbox.offendersearch.app";   // os_test_… keys only; nothing is billed
const KEY = process.env.OFFENDERSEARCH_TEST_KEY;      // os_test_…

const res = await fetch(`${BASE}/v1/search`, {
  method: "POST",
  headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ query: { firstName: "Exact", lastName: "Match", dob: "1980-01-01" } }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data.searchId, data.status, data.counts);          // srch_sbx_so_exact complete {...}
for (const rec of data.records) console.log(rec.recordId, rec.matchState, rec.registrationState, rec.source.recordUrl);
```

A response is three layers. The **envelope** says what happened to the search as a whole. The **sourceStatus** array says what happened at each registry. The **records** array holds the people found, and each record is itself four groups: identity and match, the person, the offense and registry detail, and the citation. Read them in that order and you will never mistake a partial answer for a clear one or a name-only lead for an identification. The reference pages ([record object](/docs/record-object), [matching](/docs/matching), [result completeness](/docs/result-completeness)) define every key; this page is the walk-through.

## The response envelope: searchId, status, counts and paging

*Envelope of the Exact Match response (records and sourceStatus folded)*
```json
{
  "searchId": "srch_sbx_so_exact",
  "status": "complete",
  "elapsedMs": 236,
  "counts": {
    "records": 1,
    "sourcesQueried": 58,
    "sourcesIncomplete": 0,
    "sourcesComplete": 58,
    "sourcesSkippedByScope": 0,
    "recordsReturned": 1
  },
  "capped": false,
  "cappedReason": null,
  "cappedLimit": null,
  "cappedOmittedSources": [],
  "omittedFields": [],
  "page": 1,
  "perPage": 1,
  "totalPages": 1,
  "proof": {
    "status": "none"
  },
  "warnings": [],
  "sourceStatus": [ /* 58 rows */ ],
  "records": [ /* 1 record */ ]
}
```

| Field | In this response | What it means |
| --- | --- | --- |
| `searchId` | `"srch_sbx_so_exact"` | The id of this search. Persist it: a repeated `POST` is a second billed search, and async searches are read back by this id. |
| `status` | `"complete"` | `complete` when each registry in scope finished; `partial` when at least one did not. The first field to branch on. |
| `elapsedMs` | `236` | Wall-clock time the search took, in milliseconds. Informational. |
| `counts.records` | `1` | People found across each registry in scope, before paging. |
| `counts.sourcesQueried` | `58` | Registries in scope for this search. With no `jurisdictions` filter that is the 58 US registries. |
| `counts.sourcesComplete` / `sourcesIncomplete` | `58` / `0` | How many finished and how many did not. `sourcesIncomplete > 0` is the same information as `status: "partial"`, as a number. |
| `counts.sourcesSkippedByScope` | `0` | Registries left out because the request scoped them out (`jurisdictions`, `locationScoped`). |
| `counts.recordsReturned` | `1` | Records on this page. Compare with `counts.records` to know whether to page. |
| `capped`, `cappedReason`, `cappedLimit`, `cappedOmittedSources` | `false`, `null`, `null`, `[]` | Whether the search was cut short by a result cap, why, at what limit, and which registries were omitted as a result. |
| `omittedFields` | `[]` | Record fields left out of this response. `stateData` appears here when it was not requested with `include`. |
| `page`, `perPage`, `totalPages` | `1`, `1`, `1` | Where you are in the result set. See [pagination](/docs/pagination). |
| `proof.status` | `"none"` | Whether live evidence was requested and produced. `none` on an ordinary search. |
| `warnings` | `[]` | Human-readable notes about the request. Log them; never branch on their text. |

Two of these decide everything downstream. `status` tells you whether the answer is a whole answer. `counts.records` versus `counts.recordsReturned` tells you whether you have seen it all. A clearance workflow that reads only `records.length` will call a partial search with zero hits "clear"; the fix is to read `status` first, always.

## sourceStatus: one row per registry in the response

The `sourceStatus` array has one row for each registry in scope, 58 rows on an unscoped search. The Exact Match response has 57 rows that read `"status": "complete", "matched": 0` and one that does not:

*The OH row of sourceStatus*
```json
{
  "source": "OH",
  "status": "complete",
  "matched": 1,
  "fromCache": true,
  "note": null,
  "incomplete": false,
  "incompleteReason": null,
  "cappedOmitted": false
}
```

| Field | What it means |
| --- | --- |
| `source` | The registry code, a US state or territory abbreviation. The same code appears in `record.source.jurisdiction` and `record.registrationState`. |
| `status` | Whether the registry answered. Describes the registry, not the search. |
| `matched` | How many of the returned records came from this registry. A lower bound when `incomplete` is true. |
| `fromCache` | Whether this registry was answered from the continuously updated dataset (`true`) or re-verified live for this request. |
| `incomplete` | True when this registry was not searched to the end. **This is the field to branch on**, independent of `status`. |
| `incompleteReason` | Why, as a closed enum: `deadline`, `truncated`, `excluded`, `not_searched`, `unavailable`, `error`. Non-null exactly when `incomplete` is true. |
| `note` | Free text, reworded without notice. Never branch on it. |
| `cappedOmitted` | True when the result cap dropped this registry's hits. |

The row is how you turn "zero records" into a sentence you can defend: *searched 58 registries, all complete, none matched* is a clearance; *searched 58, two incomplete because of a deadline* is not, and the incomplete results guide shows how to retry just those two.

## Identity keys and the match block of a response record

The top of every record says who this is to the API, and why it was returned for your query. Four identity keys come first, then the match block.

*Identity keys and match block (person fields folded)*
```json
"recordId": "rec_28db9639d712c4c61e8e",
"personRef": "prs_28db9639d712c4c61e8e",
"uuid": "SBX-OH-100001",
"recordType": "sex_offender",
"matchConfidence": 0.9,
"matchBasis": [
  "lastName",
  "firstName",
  "dob",
  "lastName:exact",
  "firstName:exact",
  "name_match"
],
"matchDetail": {
  "strategies": {
    "lastName": "exact",
    "firstName": "exact"
  },
  "fieldsPresent": [
    "lastName",
    "firstName",
    "dob"
  ],
  "signals": [
    "matchedLegalName"
  ],
  "matchedNameType": "legal",
  "verification": "dob_match"
},
"matchedName": {
  "value": "Exact Sample Match",
  "type": "legal"
},
…
"dobVerification": "dob_match",
"matchState": "dob_match",
"unverified": false
```

| Field | In this response | What it means |
| --- | --- | --- |
| `recordId` | `"rec_28db9639d712c4c61e8e"` | This listing. `GET /v1/records/{recordId}` fetches it again. |
| `personRef` | `"prs_28db9639d712c4c61e8e"` | The person, stable across listings and across searches. The key to deduplicate and to monitor on. |
| `uuid` | `"SBX-OH-100001"` | The registry's own identifier for the listing, as published. |
| `recordType` | `"sex_offender"` | Which schema the rest of the record follows. Always `sex_offender` on `/v1/search`. |
| `matchConfidence` | `0.9` | A 0–1 score of how well the record fits the query. A ranking signal, not an identification. |
| `matchBasis` | six strings | Which query fields contributed and how: `lastName:exact`, `firstName:exact`, `dob`, `name_match`; `alias:exact` when the hit came through an alias. |
| `matchDetail.strategies` | `lastName: exact, firstName: exact` | The comparison used per name field. |
| `matchDetail.fieldsPresent` | `lastName, firstName, dob` | The query fields that were available to match on. |
| `matchDetail.signals` | `["matchedLegalName"]` | Named reasons the record scored as it did. |
| `matchDetail.matchedNameType` | `"legal"` | `legal` or `alias`: which of the person's names your query hit. |
| `matchDetail.verification` | `"dob_match"` | The same value as `matchState`, inside the detail object. |
| `matchedName` | `{ value: "Exact Sample Match", type: "legal" }` | The name string that matched and its type. For **Alias Match** on the sandbox this is `type: "alias"`. |
| `dobVerification` | `"dob_match"` | What the date-of-birth check concluded. |
| `matchState` | `"dob_match"` | **The identification verdict.** The one field a decision should branch on. |
| `unverified` | `false` | True whenever `matchState` is anything other than `dob_match`. One boolean for the review queue. |

`matchState` takes six values. `dob_match`: the registry publishes a full date and it agrees with yours, the only automatic identification. `year_match`: the registry publishes only a year and it agrees. `age_match`: the registry publishes an age and it agrees within `ageTolerance`. `no_dob_age_year`: the registry publishes nothing to check, so the name is the only evidence. `dob_near_match`: only with `dobTolerance`, always `unverified`. `null`: you sent no `dob` or `age`, so nothing was checked. A record whose published date **contradicts** yours is not in the response at all; the sandbox demonstrates this when the same name is searched with `1990-05-05` and zero records come back. The [matching](/docs/matching) and [date of birth](/docs/date-of-birth) references, and the sibling tutorial on [no-match and multiple-match results](/docs/learn/no-match-and-multiple-match-results), cover the decision table.

## The person block and addresses in a background check API response

*Person and address fields*
```json
"name": {
  "first": "Exact",
  "middle": "Sample",
  "last": "Match",
  "suffix": "",
  "full": "Exact Sample Match"
},
"aliases": [],
"nicknames": [],
"dob": "1980-01-01",
"birthYear": 1980,
"dobPrecision": "exact",
"age": "46",
"sex": "M",
"race": "White",
"ethnicity": "Not Hispanic",
"addresses": [
  {
    "type": "residence",
    "line1": "100 Sample St",
    "city": "Sampleville",
    "county": "Sample",
    "state": "OH",
    "zipcode": "00000",
    "lat": null,
    "lng": null
  },
  { "type": "employment", … }
],
"registrationState": "OH",
"addressStates": [
  "OH"
]
```

| Field | What it means |
| --- | --- |
| `name.first` / `middle` / `last` / `suffix` / `full` | The legal name as published. `suffix` is `""` when there is none; `full` is the display string. |
| `aliases[]`, `nicknames[]` | Other names the registry lists. Empty arrays, never missing. A hit through one of these shows `matchedName.type: "alias"`. |
| `dob` | The published date of birth, `YYYY-MM-DD`, or `null` when the registry publishes none. |
| `birthYear` | The year alone, present when the registry publishes a year or a full date. |
| `dobPrecision` | How much birth evidence the registry publishes: `exact` here; `year` for year-only registries; `unknown` for the three **Name Only** sandbox records. |
| `age` | The published age, as a string. The sandbox fixture carries `"46"`. |
| `sex`, `race`, `ethnicity` | As published. Strings, `""` when absent. |
| `height`, `weight`, `eyeColor`, `hairColor`, `marks` | Physical description as published, strings. Useful for a human reviewer, never for automated identity. |
| `addresses[]` | Every address the registry lists, each with `type` (`residence`, `employment`, …), `line1`, `city`, `county`, `state`, `zipcode`, and `lat`/`lng` when geocoded. |
| `registrationState` | The registry that lists this person. The sandbox person **Out State** is registered by `OK` and lives in `TX`. |
| `addressStates[]` | Every state that appears in `addresses[]`. This is what a `state` query filters on, which is why Out State comes back for `state: "TX"`. |

Two things catch new integrations here. First, `dob` and `dobPrecision` describe what the **registry** publishes, not what you sent; the match block describes the comparison. Second, `registrationState` and `addressStates` are different questions, *who lists this person* and *where do they live or work*, and a person can answer them differently.

## Offense, stateData, flags, images and housingStatus in the response

*Offense and registry detail*
```json
"offense": {
  "crime": "Sexual battery",
  "statute": "SAMPLE 2907.03",
  "tier": "Tier III",
  "convictionDate": "2004-05-12",
  "offenseDate": "2003-11-02",
  "registrationDate": "2008-02-01",
  "releaseDate": "2008-01-15",
  "caseNumber": "SBX-2003-000101",
  "victimAge": "Minor",
  "convictionCounty": "Sample"
},
"offenses": [ { …the same object… } ],
"stateData": {
  "status": "Active",
  "designation": "Tier III sex offender",
  "lawAgency": "Sample County Sheriff's Office",
  "complianceStatus": "compliant",
  "isLifetimeRegistration": true,
  "lastVerificationDate": "2026-09-01",
  "registrationDuration": "Lifetime"
},
"flags": {
  "absconder": false,
  "predator": false
},
"images": [
  {
    "url": "https://sandbox.offendersearch.app/v1/sandbox/photos/1.svg"
  }
],
"housingStatus": "housed"
```

| Field | What it means |
| --- | --- |
| `offense` | The primary offense: `crime`, `statute`, `tier`, `convictionDate`, `offenseDate`, `registrationDate`, `releaseDate`, `caseNumber`, `victimAge`, `convictionCounty`. |
| `offenses[]` | Every offense the registry lists, same shape. `offense` is always the first of these; read `offenses` when you need them all. |
| `stateData` | Fields that only this registry publishes, under keys that vary by state: here `status`, `designation`, `lawAgency`, `complianceStatus`, `isLifetimeRegistration`, `lastVerificationDate`, `registrationDuration`. Request it with `include: ["stateData"]`; otherwise `stateData` is named in `omittedFields`. |
| `flags.absconder`, `flags.predator` | Two booleans normalised across registries: whether the registry marks the person as non-compliant or missing, and whether it applies a predator designation. |
| `images[]` | Photo URLs as `{ url }`. The sandbox serves an SVG placeholder. |
| `housingStatus` | `housed` here; `homeless` for the sandbox person **Homeless Registrant**, whose addresses carry no street. |

`offense` is the summary; `offenses[]` is the record. A reviewer's screen should render the array. `stateData` is deliberately not normalised: it is the registry's own vocabulary, and the keys under it differ from one registry to the next, so treat it as a display object rather than something to branch on.

## source and sources[]: the citation on every response record

*The citation*
```json
"source": {
  "jurisdiction": "OH",
  "registryName": "Sandbox sample registry (OH)",
  "recordUrl": "https://offendersearch.app/docs/sandbox",
  "personRef": "prs_28db9639d712c4c61e8e",
  "recordedAt": "2025-01-10T00:00:00+00:00",
  "lastCheckedAt": "2026-10-01T00:00:00+00:00",
  "sourceUpdatedAt": "2026-10-01T00:00:00+00:00"
},
"sources": [
  { …the same object, one per registry that lists this person… }
]
```

| Field | In this response | What it means |
| --- | --- | --- |
| `source.jurisdiction` | `"OH"` | The registry this listing came from. Equals `registrationState`. |
| `source.registryName` | `"Sandbox sample registry (OH)"` | The registry's display name. |
| `source.recordUrl` | `"https://offendersearch.app/docs/sandbox"` | The page a reviewer opens to confirm the listing. The citation to store with any decision. |
| `source.personRef` | `"prs_28db9639d712c4c61e8e"` | The same person key, repeated per source. |
| `source.recordedAt` | `"2025-01-10T00:00:00+00:00"` | When this listing first entered the dataset. |
| `source.lastCheckedAt` | `"2026-10-01T00:00:00+00:00"` | When the listing was last confirmed against the registry. |
| `source.sourceUpdatedAt` | `"2026-10-01T00:00:00+00:00"` | When the registry last changed the listing, where the registry publishes that. |
| `sources[]` | one entry | Each registry that lists this person, same shape. The sandbox person **Multi State** carries two entries, `FL` and `GA`, merged into one record. |

`source` is the primary citation and `sources[]` is the complete list; on a single-registry person they are the same object once. Store the URL and the two timestamps with every decision you make from the record. The sex offender API page and the [record object reference](/docs/record-object) describe the merge rule for `sources[]`.

## How the criminal records API response differs

The [criminal records API](/criminal-records-api) answers `POST /v1/criminal/search` with the same three layers, and the differences are deliberate. This is the sandbox envelope for **Felony Record** with `dob` `1975-06-15`:

*Criminal envelope (CR_FELONY_RESPONSE, record folded)*
```json
{
  "status": "complete",
  "counts": {
    "records": 1,
    "recordsReturned": 1,
    "sourcesQueried": 0,
    "sourcesComplete": 0,
    "sourcesIncomplete": 0,
    "candidatesCapped": false,
    "exclusionsIncluded": false,
    "sourcesOnFile": 1567,
    "sourcesPending": 347
  },
  "warnings": [],
  "page": 1,
  "perPage": 50,
  "totalPages": 1,
  "sourceStatus": [],
  "omittedFields": [
    "sourceData"
  ],
  "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.)."
  },
  "searchId": "srch_sbx_cr_felony",
  "sandbox": true,
  "records": [ /* 1 record */ ]
}
```

| Difference | Registry response | Criminal response |
| --- | --- | --- |
| Sources counted | `counts.sourcesQueried` is the registries in scope | `counts.sourcesOnFile` and `counts.sourcesPending` describe the dataset; `sourcesQueried` counts live sources only, so a cached search shows `0`. |
| Caps and exclusions | `capped` and `cappedReason` | `counts.candidatesCapped` and `counts.exclusionsIncluded` (true only with `include: ["exclusions"]`). |
| `legal.notice` | absent | Always present: *"Not a consumer report…"*. A hiring, tenant, credit or insurance decision goes through the [FCRA Partner API](/docs/fcra) instead. |
| `sandbox` | absent | `true` on the sandbox host, so a misrouted call is visible in the body. |
| `sourceStatus` | one row per registry | Empty on a cached search; one row per live source when a `live` block is sent. |

Inside the record the schema changes more. `dob` is an **object**, not a string; `matchBasis` and `matchDetail` are **strings**, not arrays; the offense detail lives in `cases[]`, each case carrying `charges[]` with a `disposition`, `events[]` and a `sentence`; and `sources[]` entries carry a `code` and a `level` (`county`, `state`, `federal`) instead of a `jurisdiction` and `registryName`:

*Criminal record (folded to the differing keys)*
```json
"recordType": "court_case",
"externalId": "SBX-CR-2015-000123",
"personRef": "prs_6e1aaefd188032a7e6eb",
"dob": {
  "date": "1975-06-15",
  "birthYear": 1975,
  "birthMonth": 6,
  "age": null,
  "ageAsOf": null,
  "dobPrecision": "full"
},
"currentStatus": "on_probation",
"matchConfidence": 0.99,
"matchState": "dob_match",
"dobVerification": "record",
"unverified": false,
"matchBasis": "lastName:exact firstName:exact dob:match",
"matchDetail": "name and full date of birth agree",
"cases": [
  {
    "caseNumber": "SBX-CR-2015-000123",
    "court": "Sample County Court of Common Pleas",
    "level": "county",
    "filedDate": "2015-03-02",
    "status": "Closed",
    "charges": [
      { "description": "Burglary", "severity": "felony", "disposition": "Guilty", "dispositionDate": "2015-09-14", … },
      { "description": "Theft", "severity": "misdemeanor", "disposition": "Dismissed", … }
    ],
    "sentence": { "length": "3 years", "probation": "2 years community control", "fine": 500.0, … }
  }
],
"sources": [
  {
    "code": "OH-SAMPLE",
    "level": "county",
    "recordUrl": "https://offendersearch.app/docs/sandbox",
    "lastCheckedAt": "2026-10-01T00:00:00+00:00",
    "live": false
  }
]
```

`recordType` is an open string on the criminal side: `court_case`, `jail_booking`, `doc_inmate`, `warrant`, `wanted_fugitive`, `registry`, `healthcare_exclusion`. A `jail_booking` has no disposition because a booking is not a conviction; a `court_case` is read through `cases[].charges[].disposition`. Every key is always present, with `""`, `null` or `[]` when the source publishes nothing, so a parser can be strict. The [criminal record object](/docs/criminal/record-object) and the [dispositions guide](/docs/guides/criminal/dispositions) finish the picture.

## How to read a background check API response in order

Six reads, in this order, and the response cannot mislead you.

1. **Read status first.** A `status` of `complete` means each registry in scope finished. `partial` means at least one did not, and `counts.sourcesIncomplete` tells you how many. Never treat an empty `records` array as a clearance until `status` is `complete`.
2. **Read the counts.** `counts.records` is the number of people found; `counts.recordsReturned` is how many are on this page. If they differ, there is a next page (`page`, `perPage`, `totalPages`). `capped: true` means the search was cut short and `cappedOmittedSources` lists what was left out.
3. **Scan sourceStatus.** One row per registry. Any row with `incomplete: true` is a lower bound and `incompleteReason` says why. `matched` tells you where the hits came from before you open a single record.
4. **Open each record by identity.** Key your own storage on `recordId` (this listing) and `personRef` (the person across listings). `recordType` tells you which schema the rest of the record follows.
5. **Decide on matchState.** `dob_match` is the only automatic identification. `year_match`, `age_match` and `no_dob_age_year` route to review; `null` means you sent nothing to verify against. `unverified: true` is the one boolean that summarises all of this.
6. **Keep the citation.** Store `source.recordUrl`, `source.lastCheckedAt` and `source.sourceUpdatedAt`, and every entry in `sources[]` when a person is listed by more than one registry. A decision about a person is made from the source page, not from the summary.

> **The two fields that decide.** `status` on the envelope and `matchState` on the record. Everything else in the response explains those two.

## Background check API response questions

### What does a background check API response look like?

JSON with an envelope (`searchId`, `status`, `counts`, paging), a `sourceStatus` array with one row per registry, and a `records` array. Each record carries identity keys, a match block, the person, the offense and registry detail, and a citation. The whole Exact Match answer is reproduced on this page.

### What is the difference between matchConfidence and matchState?

`matchConfidence` is a 0–1 ranking score for how well the record fits the query. `matchState` is the identification verdict from the date-of-birth check. Sort by the first; decide on the second.

### Why is a record missing even though the name matches?

Because its published date of birth contradicts the one you sent. Contradicting records are dropped, not returned with a low score. On the sandbox, Exact Match with `dob` `1990-05-05` returns `counts.records: 0`.

### What does status "partial" mean in the response?

At least one registry in scope did not finish. `counts.sourcesIncomplete` says how many, and the `sourceStatus` rows with `incomplete: true` say which and why. The records you did get are real; the registries that did not finish may hold more.

### Which field is the person's stable id?

`personRef`. It stays the same across listings and across searches, and it is what a person monitor keys on. `recordId` identifies one listing and `uuid` is the registry's own id.

### Why is dob an object in the criminal response but a string in the registry response?

Criminal sources publish birth evidence in more forms: a full date, a year, a month and year, or an age as of a date. The object carries `date`, `birthYear`, `birthMonth`, `age`, `ageAsOf` and `dobPrecision` so none of that is lost. Registries publish a date or a year, and a string with `dobPrecision` beside it is enough.

### Is the response a consumer report?

No. The criminal response says so in `legal.notice`, and the registry response is subject to the same rule. A hiring, tenant, credit or insurance decision runs as an order through the [FCRA Partner API](/docs/fcra); consult counsel on which applies to you.

## What this page does not cover

- Building the request: fields, `match` modes, `jurisdictions`, tolerances and the `live` block are in the search reference and the [background check API tutorial](/docs/learn/background-check-api-tutorial).
- The async `202` and the batch row shapes, which wrap this response: see async & webhooks and batch.
- The criminal record's 31 folded keys (`lifecycle`, `physical`, `identifiers`, `booking`, `warrant`, `supervision`, `parole`, `custody`, `exclusion`, `normalized`): see the [criminal record object](/docs/criminal/record-object).
- FCRA order results, which carry their own result objects: see FCRA results.

## Related

- [Record object reference (sex offender)](https://offendersearch.app/docs/record-object)
- [Criminal record object reference](https://offendersearch.app/docs/criminal/record-object)
- [Matching and matchState](https://offendersearch.app/docs/matching)
- [Date of birth verification](https://offendersearch.app/docs/date-of-birth)
- [Result completeness and sourceStatus](https://offendersearch.app/docs/result-completeness)
- [No-match and multiple-match results](https://offendersearch.app/docs/learn/no-match-and-multiple-match-results)
- [Background check API tutorial](https://offendersearch.app/docs/learn/background-check-api-tutorial)
- [Test a background check integration](https://offendersearch.app/docs/learn/test-a-background-check-integration)
- [Dispositions: reading cases[].charges[]](https://offendersearch.app/docs/guides/criminal/dispositions)
- [Public API for testing (the sandbox)](https://offendersearch.app/sandbox)
- [Background check API](https://offendersearch.app/background-check-api)
- [Learn APIs by example](https://offendersearch.app/docs/learn)
