search_offenders
Search US sex-offender registries for one person and/or location.
Fans out across all 58 registries (or the ones named in `jurisdictions`),
de-duplicates people across registries, scores each match, and returns the
normalized records. Supply whatever identifiers you have — all args are
optional but at least a name, location, or free-text `q` should be given.
Returns a SearchResponse object (docs/openapi.yaml / FIELD-DICTIONARY.md):
- status: 'complete' | 'partial' (partial = a source errored, or a source
could not be searched to completion)
- counts: { records, sourcesQueried, sourcesIncomplete }
- warnings: [str] — ALWAYS present, `[]` when the search ran clean
- sourceStatus: [ { source, status, matched, fromCache, note,
incomplete, incompleteReason } ] per-registry outcome
★ READ `counts.sourcesIncomplete` BEFORE REPORTING A RESULT. Above 0, `records`
is a LOWER BOUND: those registries could not be searched to the end, and an
empty or short result is NOT evidence that a person is unregistered. Say so.
`warnings[0]` states it in plain language; relay it rather than summarising it
away.
- records: [ Record ], each with:
recordId, matchConfidence, matchBasis[],
name {first, middle, last, suffix, full}, aliases[], nicknames[],
dob, dobPrecision, age, sex, race, ethnicity, height, weight,
eyeColor, hairColor, marks, addresses[{type,line1,city,county,state,zipcode,lat,lng}],
offense {crime,statute,riskLevel,tier,convictionDate,...}, flags{absconder,predator},
source {jurisdiction,registryName,recordUrl,scrapedAt,sourceUpdatedAt}, sources[],
dobVerification, unverified
(offenses[], stateData, images[] are populated only when extensive=true)
Auth: uses the caller's X-API-Key (request header or OFFENDERSEARCH_API_KEY env).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Free-text search across name/alias/address/city/state/zip (all tokens must appear). | |
| age | No | Age filter, used when DOB is unknown. Matched +/-1 year for birthday drift. | |
| dob | No | Date of birth 'YYYY-MM-DD'. Improves match precision; drives dobVerification (dob_match / year_match). | |
| lat | No | Latitude for a GIS radius search (pair with lng). | |
| lng | No | Longitude for a GIS radius search (pair with lat). | |
| city | No | Residence city filter. | |
| match | No | Fuzzy tolerance: strict=exact; balanced=nicknames/variants/initials/typos on first name, exact last; broad=also prefix/typo on last name. | balanced |
| state | No | 2-letter state/territory code (e.g. 'IL', 'PR'), or the full name. A residence FILTER — it does NOT change which registries run; all 58 are still searched. It filters as a UNION: a record is kept when it has an address in that state OR that state's registry is the one holding it. Each record carries `registrationState` and `addressStates` so you can tell which half matched; for the registration half alone, pass `jurisdictions` instead. | |
| address | No | Fuzzy street-address filter (every token must appear in some record address). | |
| zipcode | No | Residence ZIP (first 5 digits used). | |
| extensive | No | If true, request extensive detail (retains offenses[], stateData, images). A light call drops them. | |
| freshness | No | Cache/pricing tier: daily=<=24h (default), weekly=<=7d. | daily |
| last_name | No | Surname — the primary match key, e.g. 'Smith'. | |
| first_name | No | Given name, e.g. 'John'. | |
| deadline_ms | No | Max time to wait, in ms (default 120000 / 2 min). Lower for a faster bounded response; partial results return status='partial'. | |
| radius_miles | No | GIS radius in miles around lat/lng. Defaults to 1 when a point is given; capped at 100. | |
| jurisdictions | No | Registry codes to query (e.g. ['IL','TX']). Omit/null = ALL 58 registries. See get_registry_coverage for codes. | |
| location_scoped | No | OPT-IN ONLY, and never inferred from `state`. If true AND a state is set, run only the registries covering that state instead of all 58. It is a cost knob that COSTS RECALL: anyone registered by another state's registry while residing in the requested state is excluded (7.0% of records). The response reports counts.sourcesSkippedByScope and a NARROWED SEARCH warning when it applies. Leave it false unless the caller explicitly wants the cheaper, narrower search. |