Criminal Search API sandbox & test keys
A test key, the sandbox base URL, and fixed criminal test names, including the exclusion scenarios. Never billed.
Sandboxhttps://sandbox.offendersearch.appLivehttps://api.offendersearch.appCriminal search API test keys: create one in the dashboard
The criminal search sandbox is the Criminal Search API on a separate server: search, batch, stored searches, records, reports, jurisdictions, sources and resolve, with the same paths and response shapes as live. It answers from a fixed set of made-up people, so every request returns the same result every time, and nothing you send is billed.
- In the dashboard, open Developers → API keys.
- Choose Create test key. Test keys are for business accounts and need no subscription.
- Copy the key. Test keys begin
os_test_. Like a live key, the full secret is shown once, so put it in your secrets store straight away.
One test key works on every API’s sandbox. Each key in the list is labelled Test · Sandbox or Live, with the base URL it works on. Send it in the same X-API-Key header the live API uses. An account holds up to 3 active test keys and can create 10 a day. A revoked test key stops working on the sandbox within a few minutes.
Criminal search sandbox base URL vs live
| Sandbox | Production | |
|---|---|---|
| Base URL | https://sandbox.offendersearch.app | https://api.offendersearch.app |
| Key prefix | os_test_ | os_live_ |
| Data | The fixed test people listed below | The criminal records dataset |
| Billing | Never billed, never recorded to usage | Your plan’s prices |
sandbox field | true on every response | Absent |
X-Offendersearch-Environment | sandbox on every response | Absent |
Each key works on its own server only, so a mixed-up configuration is caught on the first request rather than after a release. A test key sent to the live API, or a live key sent to the sandbox, is refused with 401 and a detail message that says so:
{
"detail": "This is a test key (os_test_…). Test keys work only on the sandbox at https://sandbox.offendersearch.app, which answers with sample data. To search real records, send a live key (os_live_…) to https://api.offendersearch.app."
}{
"detail": "This is a live key. The sandbox at https://sandbox.offendersearch.app accepts test keys (os_test_…) only — create one in the dashboard under Developers → API keys. Send live keys to https://api.offendersearch.app."
}A revoked test key gets 401 with: “This test key has been revoked. Create a new test key in the dashboard under Developers → API keys.”
Each test key can send 60 requests a minute and 2,000 a day, and one IP address 120 a minute, across every API on the sandbox. Past a limit the sandbox answers 429 with a Retry-After header. See Sandbox rate limits.
Criminal search test names
Send these to POST /v1/criminal/search on the sandbox. They work the same way on POST /v1/criminal/batch, and searches and records open with GET /v1/criminal/searches/{searchId} and GET /v1/criminal/records/{recordId}. Read recordType before describing a record: an arrest is not a conviction, and a warrant means the person is sought. Field definitions are on the criminal Record object.
| Send | What comes back |
|---|---|
firstName: Clear lastName: Record | Clear record. No match: a complete answer with zero records. |
firstName: Felony lastName: Record dob: 1975-06-15 | Felony conviction. One court case: a felony burglary conviction with case number, court, charges, plea, disposition and sentence. DOB-verified. |
firstName: Felony lastName: Record | Felony conviction, no date of birth sent. The same court case, matched on name alone: matchState no_dob_age_year and unverified true, because no date of birth was sent to check. |
firstName: Felony lastName: Record dob: 1990-01-01 | Felony record name, different date of birth. No match: the record's date of birth contradicts the one sent. |
firstName: Misdemeanor lastName: Record dob: 1988-11-02 | Misdemeanor conviction. One court case: a misdemeanor theft conviction with a fine. |
firstName: Arrest lastName: Only dob: 1992-04-20 | Arrest only. One jail booking with a charge at booking and no disposition. A booking is not a conviction. |
firstName: Active lastName: Warrant dob: 1985-09-09 | Active warrant. One open warrant (currentStatus active_warrant). |
firstName: In lastName: Custody dob: 1979-12-31 | Currently in custody. One state prison record: currentlyIncarcerated true, custodyFacility, projected release date. |
firstName: Possible lastName: Match | Possible matches, name only. Three records with the same name in three states and no date of birth on any of them: possibles to review, not a confirmed identity. |
curl -X POST "https://sandbox.offendersearch.app/v1/criminal/search" \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": {
"firstName": "Felony",
"lastName": "Record",
"dob": "1975-06-15"
}
}'In a batch, each row gets the same answer it gets from POST /v1/criminal/search, in input order.
curl -X POST "https://sandbox.offendersearch.app/v1/criminal/batch" \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"firstName": "Clear",
"lastName": "Record"
},
{
"firstName": "Felony",
"lastName": "Record",
"dob": "1975-06-15"
}
]'Criminal search exclusion test names: Excluded, Sanctioned, Clear Provider
These names test exclusion-list records. Send each one with and without include: ["exclusions"] to see what a default criminal search returns and what the request adds. The exclusion block is defined on the criminal Record object.
| Send | What comes back |
|---|---|
firstName: Excluded lastName: Provider dob: 1968-03-22 | Excluded provider, conviction-based. One 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. |
firstName: Sanctioned lastName: Provider | Administrative exclusion, default search. No 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"]. |
firstName: Sanctioned lastName: Provider include: exclusions | Administrative exclusion, include exclusions. With 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. |
firstName: Clear lastName: Provider dob: 1980-01-01 include: exclusions | Clear exclusion check. No match on any exclusion list, conviction-based or administrative: a complete answer with zero records. |
Criminal reports, sources and jurisdictions in the sandbox
The rest of the Criminal Search API runs on the sandbox too, with the same paths and shapes as live. The test names above apply wherever a name is searched.
| Send | What comes back |
|---|---|
| GET /v1/criminal/sources (no key), GET /v1/criminal/jurisdictions, POST /v1/criminal/resolve | Criminal sources, jurisdictions, resolve. The production criminal service's own catalogue, jurisdiction list and website resolver, unchanged. |
| POST /v1/criminal/report {"searchId": "<a sandbox criminal search id>"} (or an inline query) | Criminal verification report. The production criminal report PDF for the sample records. Never billed. |
Criminal search sandbox examples in cURL, Node and Python
Felony conviction: Felony Record 1975-06-15, called three ways. Export your test key as OFFENDERSEARCH_TEST_KEY first.
curl -X POST "https://sandbox.offendersearch.app/v1/criminal/search" \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": {
"firstName": "Felony",
"lastName": "Record",
"dob": "1975-06-15"
}
}'Postman collection for the criminal search sandbox
One collection covers every API’s sandbox, with a folder per API, ready to import into Postman or Insomnia. The Criminal Search API folder has one request for every scenario on this page. Set {{apiKey}} to your test key; {{baseUrl}} is preset to the sandbox. A request that needs an id from an earlier response uses a variable for it: {{criminalSearchId}}.
Download the sandbox collection (JSON)
Postman: Import, then drop the file. Insomnia: Import, then From file.
Move criminal search from the sandbox to production
The sandbox and the live API differ only in the base URL and the key. Keep both in configuration, not in code, and going live is a configuration change:
# Sandbox
OFFENDERSEARCH_BASE=https://sandbox.offendersearch.app
OFFENDERSEARCH_KEY=os_test_…
# Production — change these two values and nothing else
OFFENDERSEARCH_BASE=https://api.offendersearch.app
OFFENDERSEARCH_KEY=os_live_…- Create a live key on the same Developers → API keys page.
- Remove any branch that reads
sandbox. The field does not exist on live. - Live answers are real people. Read Matching & confidence and Completeness & source status before you decide what your interface shows.
- Standard criminal search results are not a consumer report. For hiring or tenant decisions, use the FCRA API.
- Live calls are billed; see pricing.