OffenderSearch
Sandbox · free · no card

A public API for testing, with realistic records instead of placeholder data.

The OffenderSearch sandbox at sandbox.offendersearch.app answers with the production serializer’s own output for 51 documented test scenarios: scored matches, per-source status, photographs, charges and dispositions, signed webhooks. Mint a os_test_… key in the dashboard and nothing you send is billed.

Free, without limit · 60 calls a minute per keyNo code? Run the same search in the screening dashboard ›
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"
      }
    }'
Straight answer

Dummy API, fake API, or a realistic sandbox: which public API for testing you want

A dummy API returns the same hardcoded JSON for a route; a mock API adds a little logic; both exist so a front end has something to render before a back end does, and the well-known ones (placeholder posts, to-dos, products, random users) do that job well. None of them helps when the thing you are building is the integration: a screening flow that has to read a match state, a per-source status, a disposition, or a webhook signature, and has to do it correctly before a real person is on the other end.

The sandbox is the third kind. It is the real API’s code answering from a fixed set of test people, so every response has the production shape and every edge case a screening integration must handle has a name you can send: an exact match with a verified date of birth, a name-only match, an alias, a person on two registries, a clear search, a felony with a dismissed charge beside the conviction, an active warrant, someone in custody.

Dummy JSON API

Fixed responses for fixed routes. Good for rendering a list; says nothing about whether your code read the response correctly.

Mock server

You write the responses. Good for your own contract; you also wrote the bugs into it.

This sandbox

The production serializer, documented people, every scenario a screening flow meets, signed webhooks, free. A test that passes here passes against the live host.

Example

An example API for testing: one request, the exact response

Send the test name below with your test key and this is the answer, captured from the sandbox code. Keys change nothing but the host.

POST /v1/search · query { firstName: Exact, lastName: Match, dob: 1980-01-01 } · 200 OK
{
  "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": [],

Only the 56 identical “complete, matched 0” registry rows and empty fields are folded, and the fold says so. Read status and counts first, then matchState, then the citation in sources[]. The RESTful API example guide walks this exact response line by line.

Test API URL

The test API URL, the test key and the limits

Value
Base URLhttps://sandbox.offendersearch.app
KeyAn os_test_… key, in X-API-Key exactly like a live key; minted in the dashboard, up to 3 active per account
Rate60 requests a minute and 2,000 a day per key; 120 a minute per IP
BillingNone. Nothing is recorded to your usage.
ShapeProduction responses from the production serializers, plus "sandbox": true
ErrorsThe production validators: the same 422s, 401s and 429s your code will meet live
WebhooksDelivered and signed exactly as production signs them, once per scenario
  1. 01Create an account, then mint a test key on the keys page of the dashboard. No card.
  2. 02Point your client at the sandbox host, or let the SDK route an os_test_ key there automatically: pip install offendersearch · npm i @offendersearch/sdk.
  3. 03Send a documented name from the tables below. Any other name returns a complete answer with zero records.
  4. 04Swap the key for a live one when you ship. Nothing else changes.
Registry scenarios

JSON test API scenarios for the sex offender search

Names are matched on first and last name together, case-insensitively. A date of birth must be exactly as listed; a different one is a contradiction and returns no match, as in production.

ScenarioSendYou get back
Clear visitor · clearfirstName: Clear · lastName: VisitorNo match: a complete answer with zero records.
Exact match, date of birth verified · matchfirstName: Exact · lastName: Match · dob: 1980-01-01One strong match: DOB-verified (matchState dob_match), high confidence, photo, offense, registry state and two addresses.
Exact match, no date of birth sent · reviewfirstName: Exact · lastName: MatchThe same person, matched on name alone: matchState is null because no date of birth was sent to verify.
Exact match name, different date of birth · clearfirstName: Exact · lastName: Match · dob: 1990-05-05No match: the registry's date of birth contradicts the one sent.
Name only — possible matches · reviewfirstName: Name · lastName: OnlyThree possible matches in three states. None of the registries publishes a date of birth, so the name is the only evidence (medium confidence). Sending a dob returns the same three with matchState no_dob_age_year and unverified true.
Matched through an alias · reviewfirstName: Alias · lastName: MatchOne match whose legal name is different (Jordan Sample Placeholder): matchedName.type is alias, matchBasis carries alias:exact.
Registered in another state · reviewfirstName: Out · lastName: State · state: TXOne match living in Texas but registered by Oklahoma's registry: registrationState OK, addressStates [TX].
One person on two registries · reviewfirstName: Multi · lastName: StateOne record merged from two registries (Florida and Georgia): sources[] lists both.
Homeless registrant · reviewfirstName: Homeless · lastName: RegistrantOne match with housingStatus homeless.
Criminal scenarios

Test scenarios for the criminal records search

A court case with a conviction and a dismissed charge, an arrest with no disposition, a warrant, a person in custody, possible matches, and the healthcare exclusion cases.

ScenarioSendYou get back
Clear recordfirstName: Clear · lastName: RecordNo match: a complete answer with zero records.
Felony convictionfirstName: Felony · lastName: Record · dob: 1975-06-15One court case: a felony burglary conviction with case number, court, charges, plea, disposition and sentence. DOB-verified.
Felony conviction, no date of birth sentfirstName: Felony · lastName: RecordThe same court case, matched on name alone: matchState no_dob_age_year and unverified true, because no date of birth was sent to check.
Felony record name, different date of birthfirstName: Felony · lastName: Record · dob: 1990-01-01No match: the record's date of birth contradicts the one sent.
Misdemeanor convictionfirstName: Misdemeanor · lastName: Record · dob: 1988-11-02One court case: a misdemeanor theft conviction with a fine.
Arrest onlyfirstName: Arrest · lastName: Only · dob: 1992-04-20One jail booking with a charge at booking and no disposition. A booking is not a conviction.
Active warrantfirstName: Active · lastName: Warrant · dob: 1985-09-09One open warrant (currentStatus active_warrant).
Currently in custodyfirstName: In · lastName: Custody · dob: 1979-12-31One state prison record: currentlyIncarcerated true, custodyFacility, projected release date.
Possible matches, name onlyfirstName: Possible · lastName: MatchThree records with the same name in three states and no date of birth on any of them: possibles to review, not a confirmed identity.
Excluded provider, conviction-basedfirstName: Excluded · lastName: Provider · dob: 1968-03-22One exclusion-list record (recordType healthcare_exclusion, currentStatus excluded): a Medicare/Medicaid exclusion under authority 1128(a)(1), a criminal conviction, so it is returned by default. The exclusion block carries the authority, convictionBased true and the exclusion date. DOB-verified.
Administrative exclusion, default searchfirstName: Sanctioned · lastName: ProviderNo records: this person's only entry is an administrative exclusion (a license suspension, not a conviction), which a search returns only when it sends include: ["exclusions"].
Administrative exclusion, include exclusionsfirstName: Sanctioned · lastName: ProviderWith include: ["exclusions"], the administrative exclusion comes back: convictionBased false, no date of birth on the list, so matchState no_dob_age_year and unverified true. counts.exclusionsIncluded is true.
Clear exclusion checkfirstName: Clear · lastName: Provider · dob: 1980-01-01No match on any exclusion list, conviction-based or administrative: a complete answer with zero records.
Get a test key ›25 free calls, no card.
Monitors, map, FCRA

Test scenarios for webhooks, monitors, the map API and FCRA orders

ScenarioSendYou get back
Person monitor, date of birth verified201 Created. One `new` alert at once (matchLabel dob_match, jurisdiction OH), POSTed to the webhook once.
Person monitor, name only201 Created. Three `new` alerts (matchLabel name_match), one per sample registrant named Name Only.
Person monitor, one person on two registries201 Created. Two `new` alerts — one per registry that holds the person (FL and GA), as production files them.
Person monitor, nobody matches201 Created. No alerts.
Person monitor, different date of birth201 Created. No alerts: the date of birth contradicts the record.
Location monitor around the sample point201 Created. One `new` alert (matchLabel in_radius) for the sample registrant Exact Match, POSTed to the webhook once.
Location monitor anywhere else201 Created. No alerts.
Criminal person monitor201 Created. One `new` alert (matchLabel dob_match, externalId SBX-CR-2015-000123).
Criminal location monitor200 with status not_yet_available, exactly as production: nothing is created.
Send a test event (sandbox only)Files one more `new` alert on the monitor (its own first match, else the sample registrant) and POSTs it to the monitor's webhook now. Returns the alert and webhookDelivered.
A city view: clustersZoom 11 over Columbus, OH: mode clusters, 529 people in 63 clusters.
One mile around a point: named pinsZoom 15: mode markers, exactly 37 people within the mile (withinRadius 37), 15 named pins plus one approximate-area pin, each with distanceMiles.
Many people at one addressOne street-precision pin holding 14 people (a groupId, no inline members); GET /v1/map/groups/{groupId} pages through the 14, all housingStatus homeless.
An approximate-area pinNo street pins; one area pin (precision zip, label 43215) for 9 people whose registry gives only a ZIP code.
A drawn polygonshape polygon: 22 people inside, 9 pins (the 14-person address is one of them).
Open the record behind a pinThe complete record (a 'Sample …' person, Sampleville, ZIP 00000), a recordRef that opens it again later, and billing {billed: false, reason: sandbox}.
A walking route with a stop2.118 miles, 4 route units: 2 people within the 0.1-mile corridor, 1 within 1,000 feet of stop s1, and 9 more placed only to a ZIP (approximate.count 9).
Two routes in one callshape routes, 8 units, uniquePeople 2 (the same two people on both routes).
Plan and usageenabled true, sandbox true, the published rates, and zero usage this month.
Turning it on or offThe sandbox map is always on: enable and disable both answer the status with changed false, and nothing is charged.
Clear (any other last name)Order complete; every search complete with result clear and no records. Any last name not listed here behaves the same way.
Records foundOrder complete; each search furnishes one sandbox record (records_found). The sex offender record carries registry{}, the criminal record cases[] with a misdemeanor conviction.
Records withheldSearch complete with result clear: nothing furnished, only withheld counts (2 insufficient_identifiers, 1 not_current_at_source).
Sources unavailableThe search ends unavailable with result incomplete (the sources could not be reached), and the order completes. This is production's documented sandbox outcome for an FCRA order, not a simulated outage of the sandbox.
Dispute that resolvesOne furnished record, as Records. A dispute filed on it (POST /v1/fcra/orders/{id}/disputes) resolves deleted 60 seconds later, read on the next GET of the dispute; dispute.created and dispute.updated events are emitted.
Conviction-based exclusionThe criminal search furnishes one exclusion-list record (recordType healthcare_exclusion, dispositionStatus conviction, authority 1128(a)(1)). A conviction-based exclusion is furnished under the default exclusions mode, conviction_only.
Conviction-based exclusion, exclusions noneThe same subject with exclusions: "none": no exclusion-list record is furnished or counted, so the search is clear.
Administrative exclusion, default modeAn administrative exclusion (license action, not a conviction) is not furnished under the default conviction_only mode: the search is clear.
Administrative exclusion, exclusions allWith exclusions: "all" the administrative exclusion is furnished: one record, dispositionStatus sanction, convictionBased false.

A sandbox monitor is evaluated once, at creation, and its alert is POSTed to your channels.webhookUrl exactly once with the production signature; POST /v1/sandbox/monitors/{monitorId}/test-event sends one more on demand. Point it at the webhook tester to see the signed event and verify it in the browser, then follow the webhook example to do the same in your code.

Tooling

Postman, SDKs and the Try-it console for the test API

Postman collection

Every sandbox request as a collection with a sandbox environment: download the collection or fork the public workspace.

SDKs and CLI

The Python and Node clients route an os_test_ key to the sandbox automatically; npx offendersearch search "Exact Match" --dob 1980-01-01 from a terminal. SDK reference ›

Try it in the docs

Every reference page has a console that runs the request against the sandbox with your test key and shows the response, then gives you the code. Sandbox docs ›

Edges

What the sandbox does not do

  • No real people. Every record is fictional, on Sample, Example and Demo streets, with placeholder photographs. For match quality on real names, use the 25 free production calls.
  • No simulated outages. Every source answers, complete, at once; the partial-result shape is documented in result completeness rather than reproduced here.
  • No email. Monitor alerts go to your webhook only.
  • No key-less access. A test key is free and takes seconds; it is how per-key limits stay fair.
Learn

Learn the API against the sandbox

Runnable guides Every example in the learning hub runs against this sandbox.

RESTful API example ›One request and one response, line by line — this endpoint is the worked example.
Webhook example ›Receive, verify the signature and retry, in 40 lines of Express or FastAPI.
Webhook tester ›A hosted endpoint that shows our signed events and verifies them in the browser.
API token example ›Create, scope, rotate and revoke a key; sandbox keys versus live keys.
API errors ›HTTP status first, a machine code, a message you can act on — and our catalogue.
Postman documentation ›Fork the public workspace, set the sandbox environment, run the collection.
API documentation example ›What good API docs contain, with one of ours taken apart.
Free public APIs ›Where developers find public APIs, and the free public-records ones.

All guides: Learn APIs by example · reference: developer docs · packages: SDKs and CLI.

FAQ

Public API for testing: questions

Is this a free public API for testing?

Yes. The sandbox is free without limit inside the per-key rate limits, with a test key you mint yourself in a few seconds. Nothing is billed and nothing counts against your usage.

How is it different from a dummy API or a fake JSON API?

A dummy API returns fixed JSON for fixed routes so a front end has something to render. The sandbox is the production API’s own code answering from documented test people, so the responses have every production key and every edge case a screening integration must handle, including error responses and signed webhooks.

What is the test API URL?

https://sandbox.offendersearch.app for every endpoint, with a test key in X-API-Key. The SDKs route a test key there automatically; raw HTTP clients change the base URL.

Can I use it in CI?

Yes. 60 requests a minute and 2,000 a day per key is enough for a test suite; the responses are deterministic for each documented name, so assertions are stable.

Do the test scenarios cover webhooks?

Yes. A sandbox monitor sends its alert to your webhook URL once, signed exactly as production signs it, and a test-event endpoint sends another on demand. The webhook tester page receives it and verifies the signature in the browser.

Is there a public API without a key?

Not here; the key is how the sandbox keeps rate limits fair per developer. It is free and takes seconds. The free public APIs guide lists keyless APIs for other kinds of data.

Does a test that passes in the sandbox pass in production?

The response shape, validators and signatures are the same code, so a client that reads the sandbox correctly reads production correctly. What differs is the data: real people, real registries, and the partial-result cases the sandbox does not simulate.

Get a test key and send the first request

Free, no card. The sandbox answers in the production shape; swap the key when you ship.