Retrieving individual records
A search returns records; this is how you get one of them back later, which identifier to use, and what each identifier is for.
When to use this
A reviewer opens a hit from yesterday's queue; an audit asks what a cited record said; a reconciliation job checks that the person you flagged is still the same person. Each needs one record by identifier rather than a new search. GET /v1/records/{recordId} resolves a record by either identifier a search published for it — our recordId or the registry's own uuid — and is not billed. You will build the lookup, decide which identifier to store for which purpose, and handle the two non-200 answers.
Before you start
- A test key (API key only on this route). Run a sandbox search for Exact Match first; the record carries
recordIdrec_28db9639d712c4c61e8e,personRefprs_28db9639d712c4c61e8eanduuidSBX-OH-100001. - Three identifiers, three jobs: `personRef` (
prs_+ 20 hex) is the stable key for the person and what a clearance names; `recordId` (rec_+ 20 hex) is the handle for this result and what this route and a report cite; `uuid` is the registry's own number, unique only together with its jurisdiction. - A
rec_id resolves while the search that returned it is retained — 14 days — and from the platform's record index; a registryuuidresolves from the current data, and a supersededuuidstill resolves to the person's current record.
recordId. It describes the merge that produced the record for that query, so the same person can carry a different recordId on a scoped search and on a full search. Key the person on personRef; keep recordId to cite a specific result.Step by step
- Search, and keep three identifiers. From the Exact Match record store
personRef,recordIdanduuidwith the jurisdiction (sources[].jurisdiction). - Fetch by `recordId`.
GET /v1/records/rec_28db9639d712c4c61e8e. The fullRecordcomes back; the match fields are empty because nothing was matched. - Fetch by registry `uuid`.
GET /v1/records/SBX-OH-100001returns the same record. In production auuidshared by several jurisdictions answers409. - Handle `404` and `409`. A
404means this identifier was never issued for a record, or the search that issued it has aged out — never that the person is unregistered. A409means theuuidis ambiguous; userecordId.
Steps 2–3 — the lookups
curl -s https://sandbox.offendersearch.app/v1/records/rec_28db9639d712c4c61e8e -H "X-API-Key: $OFFENDERSEARCH_KEY"
curl -s https://sandbox.offendersearch.app/v1/records/SBX-OH-100001 -H "X-API-Key: $OFFENDERSEARCH_KEY"{
"recordId": "rec_28db9639d712c4c61e8e",
"personRef": "prs_28db9639d712c4c61e8e",
"uuid": "SBX-OH-100001",
"recordType": "sex_offender",
"matchConfidence": null,
"matchBasis": [],
"matchDetail": { "strategies": {}, "fieldsPresent": [], "signals": [], "matchedNameType": null, "verification": null },
"matchedName": null,
"name": { "first": "Exact", "middle": "Sample", "last": "Match", "suffix": "", "full": "Exact Sample Match" },
"dob": "1980-01-01", "birthYear": 1980, "dobPrecision": "exact", "age": "46",
"addresses": [ { "type": "residence", "line1": "100 Sample St", "city": "Sampleville", "county": "Sample", "state": "OH", "zipcode": "00000", "lat": null, "lng": null } ],
"registrationState": "OH", "addressStates": ["OH"],
"offenses": [ { "crime": "Sexual battery", "statute": "SAMPLE 2907.03", "tier": "Tier III", "convictionDate": "2004-05-12" } ],
"stateData": { "status": "Active", "designation": "Tier III sex offender", "registrationDuration": "Lifetime",
"isLifetimeRegistration": true, "complianceStatus": "compliant", "lastVerificationDate": "2026-09-01",
"lawAgency": "Sample County Sheriff's Office" },
"flags": { "absconder": false, "predator": false },
"images": [ { "url": "https://sandbox.offendersearch.app/v1/sandbox/photos/1.svg" } ],
"sources": [ { "jurisdiction": "OH", "registryName": "Sandbox sample registry (OH)", "recordUrl": "https://offendersearch.app/docs/sandbox", "lastCheckedAt": "2026-10-01T00:00:00+00:00" } ],
"matchState": null, "unverified": false,
"sandbox": true
}Two things differ from the search copy. The match fields are empty (matchConfidence: null, matchBasis: [], matchState: null): a fetch matches nothing, so there is nothing to explain. And stateData is populated here exactly as on a search — the full record is the default on both.
Step 4 — the two refusals
{ "detail": "Not found. A recordId resolves while the search that returned it is retained; run the search again to get a current recordId, or look the person up by registry uuid and state." }Registries re-issue their own numbers; one uuid can identify a registrant in Florida, another in Pennsylvania and a third in Wisconsin. The route returns one record and will not guess, so the 409 sends you to recordId, which is namespaced per jurisdiction and does not collide. The sandbox's uuids are unique, so you will not see a 409 there.
Reading the result
| Identifier | Shape | Store it for | Never use it for |
|---|---|---|---|
personRef | prs_ + 20 hex | Recognising the same person next time; clearances; monitors. Stable across re-issued registry numbers. | Citing a specific result. |
recordId | rec_ + 20 hex | Citing this result in a report, dispute or audit line; GET /v1/records/{recordId}. | Keying a person; it differs between query scopes. |
uuid + sources[].jurisdiction | registry's own string, may be "" | Cross-referencing the registry's own listing; a superseded one still resolves. | Lookups without the jurisdiction — it collides across states. |
sources[].recordUrl | URL or "" | A link for a reviewer to verify at the source. | De-duplicating or diffing. |
Every top-level key is present on every record; "" means the registry does not publish the field, null means unknown or not applicable, [] means none. The full 37-key list and the empty-value rules are in the Record object.
Edge cases and pitfalls
- `404` is about the identifier, not the person. After 14 days a
rec_id from a search can stop resolving; run the search again for a current one. Never show "not registered" on a404. - A `rec_` copy can be light. The record a
rec_id returns may be the copy a previous search served, withoutstateDatawhen that search did not ask for it. ReadstateDataas "not requested" whennullon a fetch of a light copy. - Ambiguity is real in production: tens of thousands of registry numbers are shared across jurisdictions. Prefer
recordIdfor lookups you automate. - Control characters and ids over 256 characters answer `404`, never a
5xx. - To re-open the whole search, not one record, use
GET /v1/searches/{searchId}/results(stored records, 14 days, up to 200). - Photos.
images[].urlon some registries points at the platform's media route, which also needs your key.
Production checklist
personRefis the primary key for people in your own tables;recordIdandsearchIdare stored per decision.- Lookups by
uuidalways carry the jurisdiction in your own data and tolerate a409. 404renders as "record no longer available", never as a clearance.- Re-fetches happen within the 14-day window, or a new search is run.
Related products
Product overview
- Sex offender search API
- Sex offender registry API
- Offender search API
- Background check API
- Bulk sex offender search
- Background check API pricing
- Background check API integration
- Integrations
- Free background check API
- Pay-as-you-go background check API
- Background check API in Python
- Background check API in Node.js
- NSOPW API