Offendersearch
API Reference · v1.0.0

Sex Offender Search API

A single authenticated call searches every US sex-offender registry — all 50 states, DC, the territories, and national sources — and returns scored, de-duplicated, source-tagged records. Sync-first for speed; async for guaranteed-complete deep sweeps.

Base URL https://api.offendersearch.com

Introduction

The Offendersearch API is a REST API for national sex-offender screening. It aggregates official state, territory, and national sources behind one endpoint, offers full offenders.io field parity, and adds scored matches, per-source provenance, freshness tiers, and per-registry verification reports. Every request is authenticated with an API key; every response is JSON.

  • One call, every registry. Omit jurisdictions to sweep all 57 sources, or name a subset.
  • Sync-first. A typical POST /v1/search is a cache read and returns in well under a second.
  • Provenance built in. Every record links back to the official registry record with a lastCheckedAt.
  • Additive contract. New data (e.g. criminal records) lands as a new recordType — the response shape never breaks.
Offendersearch is not a consumer reporting agency and results are not a consumer report. Do not use them for FCRA-covered decisions without appropriate process. Use is subject to our acceptable-use terms and applicable law, which you accept at onboarding.

Quickstart

Create a key in your dashboard, export it, and make your first search. Passing jurisdictions: null (or omitting it) searches all 57 registries at once.

curl https://api.offendersearch.com/v1/search \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1980-04-12"
    },
    "jurisdictions": null,
    "freshness": "standard",
    "match": "balanced",
    "include": ["stateData"]
  }'

Authentication

Authenticate every request with your secret key in the X-API-Key header. Keys are created, named, scoped, rotated, and revoked from the API keys page. A key’s secret is shown in full only once, at creation, and is hashed at rest — store it in a secret manager, never in client-side code.

Authorization header
curl https://api.offendersearch.com/v1/sources \
  -H "X-API-Key: os_live_9f2a…"

Key scopes

Each key can be scoped to the freshness tiers it may request (standard, weekly, daily) and whether it may generate billable verification reports. A request that exceeds a key’s scope is rejected with 401.

Admin operations use a separate X-Admin-Key internal credential and are not part of the public API documented here.

Errors & status codes

The API uses conventional HTTP status codes. Note that a 200 can still be a partial result: when a slow registry can’t beat your deadlineMs, its last-known rows are returned and flagged in sourceStatus rather than failing the whole call. Billing must be enabled on every account before the API returns live results.

StatusMeaningWhen you’ll see it
200OKSearch completed — or returned partial results under the deadline (check status).
202AcceptedAsynchronous search accepted; poll the results URL or await the webhook.
400Bad RequestInvalid or missing parameters (e.g. no lastName and no q/location).
401UnauthorizedMissing or invalid X-API-Key header, or a request outside the key’s scope.
402Payment RequiredBilling is not enabled on the account (required for all accounts, including verification reports).
404Not FoundThe search id or record id does not exist.
429Too Many RequestsRate limit or plan quota exceeded. Back off and retry after the Retry-After header.
504Deadline ExceededReturned only when the deadline is hit and onDeadline = "error".

Error response body

Every non-2xx response returns a JSON body with a stable error.code you can switch on and a human-readable error.message.

400 Bad Request
{
  "error": {
    "code": "invalid_request",
    "message": "query requires at least one of lastName, q, or a lat/lng radius."
  }
}

Rate limits & quotas

Requests are rate limited per API key. When you exceed your plan’s sustained rate or monthly quota the API responds with 429 Too Many Requests and a Retry-After header (seconds) telling you when to try again. Every response also carries the current limit state so you can throttle proactively.

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetUnix time when the window resets.
Retry-AfterOn a 429, seconds to wait before retrying.

Concurrency and monthly volume are set by your plan. For large batch jobs, prefer the asynchronous endpoint and spread submissions rather than firing thousands of synchronous calls at once. Need a higher ceiling? Contact us and we’ll raise your limits. Handle 429 with exponential backoff that respects Retry-After.

How a search works

Every search moves through the same four stages, so the response is predictable:

  1. Fan-out. Your query is dispatched in parallel to every registry you targeted (all 57 by default, or the codes in jurisdictions).
  2. Match & verify. Each registry’s rows are filtered by your match mode, then checked against any dob/age you supplied to confirm identity.
  3. De-duplicate & score. The same person found in multiple registries is merged into one record whose sources[] lists every corroborating registry, and each record gets a matchConfidence and matchBasis.
  4. Return. You get records, a per-registry sourceStatus, and counts — with status: "complete" or "partial".

Per-source status

Every registry the sweep touched appears in sourceStatus[] with its own status — so a slow or unavailable jurisdiction is transparent, never a silent gap:

statusMeaning
okQueried successfully; matched holds the count from this registry.
staleCached rows returned because a live refresh could not beat the deadline. Escalate to async for a live read.
pendingStill running (async sweeps) — results not yet in.
errorThe registry failed or timed out; see note.
restrictedThis registry could not be served for this request; see note.
no_coverageThe registry does not cover the queried location.

Matching & confidence

The match field sets the fuzzy-match tolerance applied uniformly across every registry, so behavior is consistent no matter which states you hit. Whatever mode you choose, providing a dob or age is the strongest lever on accuracy — it verifies identity and pushes matchConfidence toward 1.0.

ModeFirst nameLast nameUse it for
strictExact onlyExact onlyLowest false positives; pair with a DOB.
balancedNicknames, variants, initials, typos (edit-distance 1)ExactThe default — good recall with controlled noise.
broadAll balanced rulesAlso prefix + typo toleranceMaximum recall; expect more low-confidence rows to triage.

Read matchBasis to see exactly why a record matched (for example ["dob_match", "exact_name"] vs ["surname_only"]) so you can set your own confidence threshold for auto-accept vs manual review.

Freshness tiers

Freshness is a per-call field that sets the maximum acceptable data age. Fresher data means more re-verification on our side, so higher tiers carry a per-call uplift. Every record returns a lastCheckedAt so you can see exactly how current it is.

TierMax data agePriceBest for
standardUp to 3 months$0.10 / search (base only)Bulk / low-stakes screening
weeklyWithin 7 daysBase + $0.01 / searchRecurring compliance checks
dailyWithin 24 hoursBase + $0.035 / searchHigh-stakes, point-in-time decisions

Base is $0.10 per search (Standard tier = base only); Weekly freshness adds $0.01 and Daily adds $0.035 per search. See pricing for full details.

Jurisdictions & codes

The jurisdictions array controls which registries run. Omit it or send null to search every registry — the default and common case. Naming codes narrows the sweep to exactly those registries.

  • jurisdictions: null → all registries (default).
  • ["IL"] → the Illinois dedicated registry only.
  • ["IL", "NSOPW"] → Illinois and the national NSOPW source.
query.state and jurisdictions are independent. query.state is the subject’s residence — a results filter and a per-scraper hint — and does not change which registries run. Set locationScoped: true with a state to run only registries covering that state plus national sources.

Registry codes

National source: NSOPW. State & territory codes are the standard USPS abbreviations:

CodeJurisdiction
ALAlabama
AKAlaska
AZArizona
ARArkansas
CACalifornia
COColorado
CTConnecticut
DEDelaware
DCDistrict of Columbia (territory)
FLFlorida
GAGeorgia
HIHawaii
IDIdaho
ILIllinois
INIndiana
IAIowa
KSKansas
KYKentucky
LALouisiana
MEMaine
MDMaryland
MAMassachusetts
MIMichigan
MNMinnesota
MSMississippi
MOMissouri
MTMontana
NENebraska
NVNevada
NHNew Hampshire
NJNew Jersey
NMNew Mexico
NYNew York
NCNorth Carolina
NDNorth Dakota
OHOhio
OKOklahoma
OROregon
PAPennsylvania
RIRhode Island
SCSouth Carolina
SDSouth Dakota
TNTennessee
TXTexas
UTUtah
VTVermont
VAVirginia
WAWashington
WVWest Virginia
WIWisconsin
WYWyoming
PRPuerto Rico (territory)
GUGuam (territory)
USVIUS Virgin Islands (territory)
ASAmerican Samoa (territory)
CNMINorthern Mariana Islands (territory)

Sync, async & webhooks

Use POST /v1/search for the fast, common path — it returns within deadlineMs and marks any registry that couldn’t finish as stale. Use POST /v1/searches when you need a guaranteed-complete sweep including slow, browser-gated states; then poll GET /v1/searches/{searchId} or supply a webhookUrl to be notified on completion.

SynchronousAsynchronous
EndpointPOST /v1/searchPOST /v1/searches
LatencySub-second typicalSeconds to minutes
CompletenessCached rows for slow sourcesEvery source, fully
DeliveryIn the responsePoll or webhook
Best forInteractive checksDeep sweeps, batch jobs

Webhooks

Supply a webhookUrl on an async search and we POST the finished SearchResponse to it when the sweep completes — no polling required. Respond 2xx promptly to acknowledge; non-2xx responses are retried with exponential backoff for several attempts. Deliveries are signed so you can verify authenticity.

POST to your webhookUrl
{
  "event": "search.completed",
  "searchId": "srch_9f2a7c",
  "status": "complete",
  "counts": { "records": 3, "sourcesQueried": 57, "sourcesStale": 0 },
  "records": [ /* … scored, de-duplicated records … */ ]
}
Verify the X-Offendersearch-Signature header (an HMAC of the raw request body using your signing secret) before trusting a webhook, and treat delivery as at-least-once — de-duplicate on searchId.

Idempotency

Network retries should never start — or bill — a second search. Send an Idempotency-Key header (any unique client-generated string, e.g. a UUID or your order id) on POST /v1/searches. If we’ve already seen that key, we return the original job instead of creating a new one, so a timeout-and-retry is safe.

Safe retry
curl https://api.offendersearch.com/v1/searches \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Idempotency-Key: 7f0c2e14-order-4821" \
  -H "Content-Type: application/json" \
  -d '{ "query": { "lastName": "Doe" }, "freshness": "daily" }'

Reuse the same key when retrying the same logical request; use a fresh key for a genuinely new search. Keys are scoped to your account and retained long enough to cover normal retry windows.

Pagination

POST /v1/search returns the full, de-duplicated result set for a query in a single response — there is no page cursor to manage for a normal identity search, because matches are already scored and merged across registries.

  • Broad geographic sweeps. A wide lat/lng + radiusMiles query can match many registrants. Narrow the radius or add name filters, or run it as an async search for a guaranteed-complete set.
  • offenders.io compatibility. The compat endpoint preserves their paged envelope — pass page and read page / totalPages from the response exactly as before.

Verification report

A branded, timestamped PDF/HTML report of your search results that cites the originating registry and links back to the official record — a defensible artifact for your audit file. It is your report of public-record data, clearly labeled as Offendersearch output; it does not reproduce, mirror, or impersonate any government website. It is available to every account and billed per document, for only the registries you explicitly name. Request it inline on a search via the proof field, or after the fact via POST /v1/searches/{searchId}/proof.

proof field
{
  "registries": ["NJ", "NSOPW"],
  "format": "pdf"
}

Security & HIPAA

Offendersearch is built for compliance-sensitive customers. All API traffic is served over TLS and data is encrypted at rest; API keys are per-account and stored only as one-way hashes; access to production data is least-privilege; and every request is logged for audit. A Business Associate Agreement (BAA) is available to eligible enterprise accounts that process PHI through the API.

  • Encryption. TLS in transit, AES-256 at rest for stored records and reports.
  • Per-key hashing. Secrets are hashed at rest — a database read can never expose a usable key.
  • Access controls & tenant isolation. Data is scoped per account; one customer can never read another’s keys, usage, or reports.
  • Audit logging. Requests are logged with account, timestamp, and endpoint; usage is visible to account owners.
  • Honest posture. BAA available and a formal SOC 2 examination is in progress — we don’t claim certifications we don’t hold.
Full details, the BAA request flow, and our data-handling model are on the HIPAA & Security page. Registry results are public-record data and are not a consumer report; do not use them for FCRA-covered decisions without appropriate process.

Migrating from offenders.io

Point existing integrations at POST /v1/compat/sexoffender. It accepts offenders.io’s exact parameters and returns their exact { offenders, page, totalPages } envelope — switch by changing only the base URL and key, with no changes to your request or response handling. When you’re ready, move to /v1/search for scored matches, provenance, freshness tiers, and verification reports.

Parameter map

Every offenders.io input has a home in our contract:

offenders.ioOffendersearchNotes
firstName / lastNamequery.firstName / query.lastNameSame fields.
dobquery.dobYYYY-MM-DD. Used as a verifier and to boost matchConfidence.
city / state / zipcodequery.city / query.state / query.zipcodeResidence filters.
addressquery.addressFuzzy street match.
lat / lng / radiusquery.lat / query.lng / query.radiusMilesGIS radius (miles, max 100).
qquery.qFree-text across name, aliases, city, ZIP, address.
fuzzy: truematch: "balanced"Fuzzy is a mode with us, applied uniformly across registries.
prefixMatchmatch: "broad"Broad adds prefix + typo tolerance on the last name.
mode: "extensive"include: ["stateData"]Full offenses[], photos, vehicles, state-specific fields.
uuid / personUuidGET /v1/records/{recordId}Direct record lookup by id.
pagepage (compat only)Native /v1/search returns the full de-duplicated set; use async for very large sweeps.
createdAt* / updatedAt* filterssource.scrapedAt / source.sourceUpdatedAtWe return these timestamps on every record; date-range request filters are on the roadmap.
faceId (Facial Search)Not offered today; on the roadmap. Not part of the current contract.

What you gain

Moving from compat to /v1/search adds capabilities offenders.io doesn’t offer:

  • Scored matches. Every record carries matchConfidence (0–1) and matchBasis. offenders.io returns raw rows with no score.
  • Identity verification. dobVerification and unverified tell you exactly how each record was checked against your DOB/age.
  • De-duplicated people. One record per person with a sources[] array of every corroborating registry — not repeated rows.
  • Per-source status + partial results. sourceStatus reports each registry (ok / stale / error / …) instead of a silent black box.
  • Freshness tiers. standard / weekly / daily control data age; every record reports its true lastCheckedAt.
  • Verification reports. Branded, timestamped PDF/HTML audit artifacts per registry, on demand.
  • Sync-first speed. One blocking call returns scored results — typically sub-second — with async available for deep sweeps.

The Record object

Every match in a search response is a normalized Record. The same schema is returned by /v1/search and /v1/records/{id}. Key fields:

FieldTypeDescription
recordId
optional
stringStable identifier for the normalized record (use with GET /v1/records).
uuid
optional
stringStable per-record UUID (parity with offenders.io uuid / personUuid).
recordType
optional
enumsex_offender today; additive as new types ship.
matchConfidence
optional
numberOur score from 0 to 1. offenders.io returns none.
matchBasis
optional
string[]Why it matched: dob_match, exact_name, nickname, age_match, surname_only, …
name
optional
Namefirst, middle, last, suffix, and a pre-formatted full.
aliases
optional
string[]Known alternate names and spellings.
dob
optional
date | nullWith dobPrecision: exact · year · year_month · unknown.
age
optional
stringCaptured when a DOB is not published.
sex / race / ethnicity
optional
stringDemographics where the registry publishes them.
height / weight / eyeColor / hairColor / marks
optional
stringPhysical description.
addresses
optional
Address[]residence · employment · school · other, each with line1, city, county, state, zipcode, and optional lat/lng.
offense
optional
OffensePrimary offense: crime, statute, tier, riskLevel, convictionDate, registrationDate, releaseDate, caseNumber, victim details.
offenses
optional
Offense[]The full offense list (returned with include: ["stateData"]).
stateData
optional
StateDataExtended per-state detail — stateOffenderId, status, designation, registrationEnds, verificationRequirement, lawAgency, judgmentOfConvictionUrl, vehicles[], photos[].
flags
optional
objectabsconder and predator (nullable booleans).
images
optional
object[]Photo URLs where the registry publishes them.
source
optional
SourceRefThe primary registry: jurisdiction, registryName, recordUrl, scrapedAt, lastCheckedAt, sourceUpdatedAt.
sources
optional
SourceRef[]Every registry that corroborated this person (for de-duplicated records).
dobVerification
optional
enumHow identity was checked: dob_match · age_match · unverified_no_dob_or_age.
unverified
optional
booleanTrue when the record had no DOB and no age to verify against — shown anyway, flagged.
Example
Record
{
  "recordId": "rec_4b1e",
  "recordType": "sex_offender",
  "matchConfidence": 0.98,
  "matchBasis": ["dob_match", "exact_name"],
  "name": { "first": "John", "middle": "A", "last": "Doe", "full": "John A. Doe" },
  "dob": "1980-04-12",
  "dobPrecision": "exact",
  "sex": "male",
  "addresses": [
    { "type": "residence", "city": "Trenton", "state": "NJ" }
  ],
  "offense": { "tier": "II", "statute": "2C:14-2" },
  "flags": { "absconder": false, "predator": false },
  "dobVerification": "dob_match",
  "unverified": false,
  "source": {
    "jurisdiction": "NJ",
    "registryName": "NJ Sex Offender Internet Registry",
    "recordUrl": "https://www.icrimewatch.net/...",
    "lastCheckedAt": "2026-07-25T09:14:00Z"
  },
  "sources": [
    {
      "jurisdiction": "NJ",
      "registryName": "NJ Sex Offender Internet Registry",
      "recordUrl": "https://www.icrimewatch.net/...",
      "lastCheckedAt": "2026-07-25T09:14:00Z"
    }
  ]
}
POST/v1/searchesAuth: X-API-Key

Asynchronous search

Kicks off a guaranteed-complete sweep and returns immediately with a search id. Use it when completeness matters more than latency — including slow, browser-gated states.

The async endpoint accepts every field the synchronous search accepts, plus an optional webhookUrl. Instead of waiting, it returns 202 Accepted with a searchId and a resultsUrl right away and keeps working in the background until every named jurisdiction has responded.

Retrieve results by polling GET /v1/searches/{searchId} until status is complete, or supply a webhookUrl and we will POST the finished SearchResponse to it — no polling required.

Send an Idempotency-Key header to make retries safe: a repeated key returns the original job instead of starting (and billing) a second search. See Idempotency.

What you can do
  • Guaranteed completeness. Waits for slow browser-gated registries instead of returning them as stale.
  • Webhook or poll. Get notified via webhookUrl, or poll the results URL on your own schedule.
  • Same query surface. All query fields, jurisdictions, match, freshness, include, and verification-report options carry over.
  • Safe retries. An Idempotency-Key header collapses duplicate submissions to one job and one bill.
Parameters · Headers
FieldTypeDescription
X-API-Key
required
stringYour secret API key.
Idempotency-Key
optional
stringOptional. A unique client token; retries with the same value return the original job.
Body
FieldTypeDescription
…SearchRequest
required
objectEvery field from POST /v1/search is accepted (query, jurisdictions, match, freshness, include, proof, …).
webhookUrl
optional
uriWe POST the completed SearchResponse here when the sweep finishes.
Request
curl https://api.offendersearch.com/v1/searches \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f0c2e14-order-4821" \
  -d '{
    "query": { "lastName": "Doe" },
    "jurisdictions": null,
    "freshness": "daily",
    "webhookUrl": "https://acme.example/hooks/os"
  }'
Response
200 OK
{
  "searchId": "srch_9f2a7c",
  "status": "running",
  "jurisdictionsQueried": 57,
  "estimatedCompletionSec": 45,
  "resultsUrl": "https://api.offendersearch.com/v1/searches/srch_9f2a7c"
}

Returns 202 Accepted. status is "pending" or "running"; poll resultsUrl or wait for the webhook.

POST/v1/searches/{searchId}/proofAuth: X-API-Key

Generate a verification report

A branded, timestamped PDF/HTML report of your search results that cites the originating registry and links back to the official record — a defensible artifact for your audit file. It is your report of public-record data, clearly labeled as Offendersearch output; it does not reproduce, mirror, or impersonate any government website.

A verification report is generated for only the registries you name in registries (the API field is still called proof), and each document is billed separately. It is available to every account (billing must be enabled). Omit it entirely and there is no extra charge.

You can request a verification report inline on a search via the proof field, or after the fact against a completed searchId with this endpoint.

What you can do
  • Per-registry, on demand. Name exactly which registries to include — never all sources implicitly.
  • PDF or HTML. Choose the format that fits your audit workflow.
  • Audit-ready. Each report is clearly labeled as Offendersearch output, cites the originating registry, and links back to the official record.
Path parameters
FieldTypeDescription
searchId
required
stringThe search to generate a verification report for.
Body
FieldTypeDescription
registries
required
string[]Registry codes to render (min 1), e.g. ["IL","NSOPW"]. Each document is billed.
format
optional · default "pdf"
"pdf" | "html"Output format for every document in the bundle.
Request
curl https://api.offendersearch.com/v1/searches/srch_9f2a7c/proof \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "registries": ["NJ", "NSOPW"], "format": "pdf" }'
Response
200 OK
{
  "status": "ready",
  "billedDocuments": 2,
  "documents": [
    { "registry": "NJ",    "format": "pdf", "url": "https://files.offendersearch.com/proof/nj-….pdf" },
    { "registry": "NSOPW", "format": "pdf", "url": "https://files.offendersearch.com/proof/nsopw-….pdf" }
  ]
}

Returns 402 Payment Required if billing is not enabled on the account.

GET/v1/records/{recordId}Auth: X-API-Key

Get a record

Fetch a single normalized record by id — for re-displaying or refreshing a specific person you already found.

Use the recordId (or uuid) returned in a search result — this is our equivalent of offenders.io’s uuid / personUuid lookup. Returns the full Record object, including every source that corroborated the person.

Path parameters
FieldTypeDescription
recordId
required
stringThe recordId (or uuid) from a search result.
Request
curl https://api.offendersearch.com/v1/records/rec_4b1e \
  -H "X-API-Key: $OFFENDERSEARCH_KEY"
Response
200 OK
{
  "recordId": "rec_4b1e",
  "uuid": "b1e4-…",
  "recordType": "sex_offender",
  "name": { "first": "John", "middle": "A", "last": "Doe", "full": "John A. Doe" },
  "dob": "1980-04-12",
  "dobPrecision": "exact",
  "dobVerification": "dob_match",
  "unverified": false,
  "source": {
    "jurisdiction": "NJ",
    "registryName": "NJ Sex Offender Internet Registry",
    "recordUrl": "https://www.icrimewatch.net/...",
    "lastCheckedAt": "2026-07-25T09:14:00Z"
  }
}

Returns 404 if the recordId does not exist.

GET/v1/sourcesAuth: X-API-Key

List sources

The coverage catalog: every jurisdiction we cover, by code, name, scope, and live health.

A public, keyed view of every jurisdiction we cover, with its code, display name, and scope (state or national), plus lightweight health signals such as typical latency and last successful refresh. Use it to render your own coverage UI or to decide which jurisdictions to name.

What you can do
  • Coverage. Every jurisdiction with its code, name, and scope (state/national).
  • Health. Typical latency and last-success timestamps so you can see what’s live.
Request
curl https://api.offendersearch.com/v1/sources \
  -H "X-API-Key: $OFFENDERSEARCH_KEY"
Response
200 OK
[
  {
    "id": "NJ",
    "name": "NJ Sex Offender Internet Registry",
    "covers": ["NJ"],
    "scope": "state",
    "health": { "lastSuccessAt": "2026-07-25T09:14:00Z", "typicalLatencyMs": 380 }
  },
  {
    "id": "NSOPW",
    "name": "National Sex Offender Public Website",
    "covers": ["US"],
    "scope": "national"
  }
]
POST/v1/compat/sexoffenderAuth: X-API-Key

offenders.io compatibility

A drop-in endpoint that accepts offenders.io’s exact parameters and returns their exact { offenders, page, totalPages } envelope.

Point an existing offenders.io integration here and it keeps working by changing only the base URL and API key — no code changes to your request or response handling.

Under the hood it maps onto the same engine, so you can migrate incrementally: run on compat today, then move to /v1/search when you want scored matches, provenance, and freshness tiers. See Migrating from offenders.io for the full parameter map.

Parameters · Body (offenders.io parameters, verbatim)
FieldTypeDescription
firstName / lastName
optional
stringName fields.
dob / city / state / zipcode
optional
stringStandard filters.
address
optional
stringFuzzy street match.
lat / lng / radius
optional
numberGIS radius search (radius in miles, max 100).
q
optional
stringFree-text query.
fuzzy
optional
booleanEnable fuzzy name matching (maps to match: "balanced").
mode
optional
"extensive"Request extended per-state detail (maps to include: ["stateData"]).
prefixMatch
optional
"firstName" | "lastName"Prefix-match a name field (maps to match: "broad").
uuid / personUuid
optional
stringDirect record lookup (maps to GET /v1/records/{id}).
page
optional
integerPage number for the paginated envelope.
Request
curl https://api.offendersearch.com/v1/compat/sexoffender \
  -H "X-API-Key: $OFFENDERSEARCH_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "John", "lastName": "Doe", "state": "NJ", "fuzzy": true, "mode": "extensive" }'
Response
200 OK
{
  "offenders": [ /* offenders.io-shaped records */ ],
  "page": 1,
  "totalPages": 1
}

The response is the offenders.io envelope, returned verbatim. Move to /v1/search for scored matches, provenance, freshness, and verification reports.