Search scope
Where an order searches and why: the plan, the policy, the gaps, and the identity layer.
Livehttps://api.offendersearch.appSandboxhttps://sandbox.offendersearch.appHow the scope is built
Every order carries a search plan: the counties, states and national tier it searches, why each is there, and the sources behind each. The plan is computed from the subject’s address history and the order’s use location before anything runs, stored on the order, shown in the report, and it decides which sources the live discovery checks.
- Each address inside the lookback window is placed in a county — by the county you name, else by the ZIP, else by the city (a city that straddles a county line lists every county it touches).
- The counties around each residence county come in: those whose internal point lies within
radiusMiles, nearest first, up tomaxNeighborCounties. County lines and state lines do not stop it. - The use location’s county and state come in; the states of every in-scope county come in; the national tier is always in.
- Each jurisdiction is matched to the sources we hold for it — county, city and regional criminal sources by county, statewide sources by state, the federal tier, and the sex offender registry of each state. A source added to the catalog with its geography appears in every later plan with no change on your side.
- A county with no county-level source is reported as a gap, with the statewide sources that still cover it. The result is unchanged unless the policy sets
requireCountyCoverage.
Preview before you order
POST /v1/fcra/search-plans returns the plan for an address history, a use location and the searches you would order, without placing an order: nothing is stored, nothing is metered, no evidence row is written. Use it to show a reviewer where a search will go, or to decide whether to widen it.
curl -X POST "https://api.offendersearch.app/v1/fcra/search-plans" \
-H "X-API-Key: $OFFENDERSEARCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"endUserId": "eu_0b44acbeefeaa7d7",
"addresses": [
{ "state": "OH", "city": "Columbus", "postalCode": "43215", "from": "2019-01" },
{ "state": "TX", "postalCode": "76102", "from": "2012-01", "to": "2017-06" }
],
"useLocation": { "state": "OH", "city": "Dublin" },
"searches": [{ "type": "sex_offender" }, { "type": "criminal" }],
"scopePolicy": { "maxNeighborCounties": 3 }
}'Preview an FCRA search plan — POST /v1/fcra/search-plans request and response
Where an order would search for an address history and a use location: the counties, states and national tier in scope, why, the sources behind each, and the gaps. Nothing is stored or metered.
Request
| Field | Type | Description |
|---|---|---|
| endUserId optional | string or null | An end user whose scope policy is the base for this plan. |
| addresses optional | array of SubjectAddress or null | The subject's address history, up to 20. |
| useLocation optional | UseLocation or null | Where the job, the housing or the transaction is. |
| searches required | array of SearchSpec | The searches the order would carry, one to three. |
| scopePolicy optional | ScopePolicy or null | Overrides on top of the end user's policy. |
Response
The plan.
The body is the SearchPlan object. The plan: every address as it was read, every jurisdiction in scope with its reasons and sources, the gaps, and the totals.
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key (the body is { "detail": "…" }, a string). |
| 403 | fcra_not_enabled | This account has no active FCRA partner agreement. |
| 403 | fcra_not_on_trial | FCRA orders are not available on a trial account. |
| 404 | unknown_end_user | No such end user on this account. |
| 422 | — | A field failed validation (the standard validation body), including an address source other than declared. |
The SearchPlan object — where an order searches and why
The plan: every address as it was read, every jurisdiction in scope with its reasons and sources, the gaps, and the totals.
Returned or accepted by POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| version | string | The scope engine version the plan was computed with. Example "scope-2026-10-26.1" |
| policy | ScopePolicy | The effective policy, every field filled in. |
| computedAt | string (date) | The day the plan was computed (the lookback window counts from it). |
| searchTypes | array of string one of "sex_offender", "criminal", "healthcare" | The searches the plan is for. |
| addresses | array of ScopeAddress | Every address from the request, as read. |
| jurisdictions | array of ScopeJurisdiction | Counties first, then states, then the national tier. |
| gaps | array of ScopeGap | Counties in scope with no county-level source. |
| excluded | array of string | Jurisdiction ids your policy removed. |
| summary | object | The totals. |
| summary.counties | integer | Counties in scope. |
| summary.states | integer | States in scope. |
| summary.sources | object | Sources behind the plan. |
| summary.sources.live | integer | Sources searchable today. |
| summary.sources.pending | integer | Sources mapped and coming soon. |
| summary.gaps | integer | Counties in scope with no county-level source. |
| summary.unresolvedAddresses | integer | Addresses that could not be placed in a county (no usable ZIP, city or county). |
| healthcare | object | Present when the plan includes a healthcare search: every healthcare source in every state (not tied to the consumer's addresses). |
| healthcare.scope | string one of "all_states" | Always all_states. |
| healthcare.sources | array of object | Each source: id, name, state, scope, status (live / pending), recordKinds, live, identifiers, matchOn, freshness. |
| healthcare.statesCovered | array of string | States with at least one live healthcare source. |
| healthcare.statesWithoutSource | array of string | States with none yet — disclosed, like a county gap. |
| healthcare.sourceCounts | object | Totals. |
| healthcare.sourceCounts.live | integer | Sources searchable today. |
| healthcare.sourceCounts.pending | integer | Sources mapped and coming soon. |
The ScopeAddress object — one address as the plan read it
One address from the request, with how it was placed in a county and whether it fell inside the lookback window.
Returned or accepted by POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| index | integer | Its position in the request's addresses. |
| state | string or null | The state, upper-cased. |
| city | string or null | As sent. |
| postalCode | string or null | As sent. |
| county | string or null | The county it resolved to, or the county name as sent. |
| from | string or null | As sent. |
| to | string or null | As sent. |
| source | string one of "declared", "document", "traced" | Where the address came from. declared today; document and traced are reserved for the identity layer. |
| inLookback | boolean | Whether the address counts under the policy's lookbackYears. |
| resolvedBy | string one of "county", "zip", "city", "unresolved" | What placed it in a county: a named county, the ZIP, the city, or nothing (unresolved). |
| candidateCounties | array of object | Every county the address may be in (a ZIP that crosses a county line, a city in several), up to six. |
| candidateCounties[].fips | string | County FIPS code. |
| candidateCounties[].state | string | State. |
| candidateCounties[].name | string | County name. |
| candidateCounties[].id | string | The jurisdiction id (OH-FRANKLIN). |
The ScopeJurisdiction object — one county, state or the national tier
One jurisdiction in the plan with why it is there and the sources behind it.
Returned or accepted by GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| id | string | <ST>-<COUNTY> for a county, the state code for a state, US for the national tier.Example "OH-FRANKLIN" |
| level | string one of "county", "state", "national" | county for a county court or booking source, state for a statewide repository, national for a source that is not tied to one place. |
| state | string or null | The state; null for the national tier. |
| county | string or null | The county name; null unless level is county. |
| fips | string or null | The county FIPS code; null unless level is county. |
| reasons | array of ScopeReason | Why it is in scope. |
| sources | array of ScopeSource | The sources the plan searches for it. |
| sourceCounts | object | How many of this jurisdiction's sources answer live today (live) and how many are known but not yet searched live (pending). |
| sourceCounts.live | integer | Live sources. |
| sourceCounts.pending | integer | Pending sources. |
| gap | boolean | Present and true on a county with no live county-level source; its statewide sources still apply (see gaps). |
The ScopeReason object — why a jurisdiction is in scope
One reason a county or state is in the plan. Only the keys that apply to the reason are present.
Returned or accepted by GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| kind | string one of "residence", "neighbor", "use_location", "client_added", "always" | residence (the subject lived there), neighbor (near a residence county), use_location, client_added (your policy), always (the national tier). |
| address | integer or null | The address index this reason comes from. |
| source | string or null | The address's source. |
| from | string or null | The address's from. |
| to | string or null | The address's to. |
| resolvedBy | string or null | How the address was placed (county, zip, city). |
| of | string or null | For neighbor: the residence jurisdiction it neighbours. |
| miles | number or null | For neighbor: miles between the two counties' internal points. |
The ScopeSource object — a source behind a jurisdiction
One source the plan will search for a jurisdiction: a criminal source from the source registry, or a sex offender registry.
Returned or accepted by GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| product | string one of "criminal", "sex_offender", "healthcare" | Which search uses it. |
| id | string | The public source code, the same value as sources[].code on a record.Example "OH-FRANKLIN" |
| name | string or null | The jurisdiction label. |
| scope | string | county, city, regional, statewide, federal, state or national. |
| status | string one of "live", "pending" | live (searchable today) or pending (mapped, coming soon). |
| recordKinds | array of string | The kinds of record the source holds (criminal sources). |
| live | boolean | Whether a live check can run against it. |
| yearsBack | integer or null | How many years of records the source states it holds; null when unknown. |
The ScopeGap object — a county with no county-level source
A county in scope for which we hold no county-level source, and what still covers it.
Returned or accepted by GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| id | string | The jurisdiction id. |
| county | string | The county name. |
| state | string | The state. |
| coveredBy | array of string | The live statewide criminal sources that still cover the county. |
| pendingSources | array of string | County-level sources mapped for it and coming soon. |
The scope policy
A scope policy says how far beyond the subject’s own counties an order searches. Set it once on an end user (scopePolicy on POST /v1/fcra/end-users or PATCH); every order for that end user uses it, and an order may override any field with its own scopePolicy. Every field is optional.
| Field | Default | What it does |
|---|---|---|
lookbackYears | 7 | An address whose to date is older is out of scope. An address with no dates always counts. |
radiusMiles | 25 | How far around each residence county to look for neighbouring counties. |
neighborCounties | true | Whether neighbouring counties come in at all. |
maxNeighborCounties | 6 | At most this many neighbours per residence, nearest first. |
includeUseLocation | true | Whether the use location’s county and state come in. |
statewide | true | Whether the statewide sources of every in-scope state are searched. |
national | true | Whether the national tier is searched. |
requireCountyCoverage | false | When true, a county with no county-level source makes the search incomplete rather than a reported gap. |
extraJurisdictions | — | Counties or whole states to add. |
excludeJurisdictions | — | Counties or whole states to leave out. A county the subject lived in can never be removed. |
The ScopePolicy object — where an order searches
Where an order searches beyond the subject's own counties. Set it once on an end user; override any field on an order. Every field is optional and the defaults are in each description.
Returned or accepted by POST /v1/fcra/end-users, GET /v1/fcra/end-users, GET /v1/fcra/end-users/{end_user_id}, PATCH /v1/fcra/end-users/{end_user_id}, POST /v1/fcra/orders, GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| lookbackYears optional | integer or null | How many years back an address counts, 1 to 30. An address whose to date is older is out of scope. Default 7.Example 7 |
| radiusMiles optional | number or null | How far around each residence county to look for neighbouring counties, 0 to 100. Default 25. Example 25 |
| neighborCounties optional | boolean or null | Whether neighbouring counties come into scope at all. Default true. |
| maxNeighborCounties optional | integer or null | At most this many neighbours per residence, nearest first, 0 to 20. Default 6. Example 6 |
| includeUseLocation optional | boolean or null | Whether the use location's county and state come into scope. Default true. |
| statewide optional | boolean or null | Whether the statewide sources of every in-scope state are searched. Default true. |
| national optional | boolean or null | Whether the national tier (federal sources) is searched. Default true. |
| requireCountyCoverage optional | boolean or null | When true, a county in scope with no county-level source makes the search incomplete instead of a reported gap. Default false. |
| extraJurisdictions optional | array of ScopeTarget or null | Counties or whole states to add to the scope. |
| excludeJurisdictions optional | array of ScopeTarget or null | Counties or whole states to leave out. A county the subject lived in can never be removed. |
The ScopeTarget object — a county or state to add or remove
A county (by name) or a whole state, named in a scope policy's extraJurisdictions or excludeJurisdictions.
Returned or accepted by POST /v1/fcra/end-users, GET /v1/fcra/end-users, GET /v1/fcra/end-users/{end_user_id}, PATCH /v1/fcra/end-users/{end_user_id}, POST /v1/fcra/orders, GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/search-plans, GET /v1/fcra/orders/{order_id}/scope.
| Field | Type | Description |
|---|---|---|
| state required | string | Two-letter state code, any case. Example "KY" |
| county optional | string or null | The county name in any common form (Jefferson, Jefferson County, Orleans Parish). Omit it to name the whole state.Example "Jefferson" |
The scope on an order and in the report
The order object carries scope: the totals and one line per jurisdiction. GET /v1/fcra/orders/{id}/scope returns the full plan, addresses included. The report carries the same plan without the addresses, so whoever reads a clear can see exactly which counties, states and sources it rests on.
Retrieve an FCRA order's search plan — GET /v1/fcra/orders/{order_id}/scope
The full plan the order ran under: every county and state in scope, why, and the sources behind each.
Request
| Field | Type | Description |
|---|---|---|
| order_id required | string | The order id (fo_…).Example "fo_ee0b34c5b52fd35b" |
Response
The plan.
| Field | Type | Description |
|---|---|---|
| orderId | string | The order id. |
| scope | SearchPlan | The plan. |
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key (the body is { "detail": "…" }, a string). |
| 403 | fcra_not_enabled | This account has no active FCRA partner agreement. |
| 403 | fcra_not_on_trial | FCRA orders are not available on a trial account. |
| 404 | not_found | No such order on this account. |
The ScopeSummary object — an order's scope in brief
The scope on an order: the totals and one line per jurisdiction. The full plan is GET /v1/fcra/orders/{order_id}/scope.
Returned or accepted by POST /v1/fcra/orders, GET /v1/fcra/orders, GET /v1/fcra/orders/{order_id}, POST /v1/fcra/orders/{order_id}/cancel, POST /v1/fcra/orders/{order_id}/adjudication.
| Field | Type | Description |
|---|---|---|
| version | string | The scope engine version. Example "scope-2026-10-26.1" |
| counties | integer | Counties in scope. |
| states | integer | States in scope. |
| sources | object | Sources behind the plan. |
| sources.live | integer | Sources searchable today. |
| sources.pending | integer | Sources mapped and coming soon. |
| gaps | integer | Counties in scope with no county-level source. |
| unresolvedAddresses | integer | Addresses that could not be placed in a county (no usable ZIP, city or county). |
| jurisdictions | array of object | One line per jurisdiction. |
| jurisdictions[].id | string | The jurisdiction id. |
| jurisdictions[].level | string one of "county", "state", "national" | county, state or national. |
| jurisdictions[].gap | boolean | true on a county with no live county-level source. |
The ReportScope object — the scope as stated in a report
The plan the report ran under, without the addresses: jurisdictions, sources, gaps and totals. A reader holds a clear against exactly this.
Returned or accepted by GET /v1/fcra/orders/{order_id}/report.
| Field | Type | Description |
|---|---|---|
| version | string | The scope engine version. |
| policy | ScopePolicy | The effective policy. |
| jurisdictions | array of ScopeJurisdiction | Every jurisdiction searched. |
| gaps | array of ScopeGap | Counties with no county-level source. |
| excluded | array of string | Jurisdiction ids the policy removed. |
| summary | object | The totals. |
| summary.counties | integer | Counties in scope. |
| summary.states | integer | States in scope. |
| summary.sources | object | Sources behind the plan. |
| summary.sources.live | integer | Sources searchable today. |
| summary.sources.pending | integer | Sources mapped and coming soon. |
| summary.gaps | integer | Counties in scope with no county-level source. |
| summary.unresolvedAddresses | integer | Addresses that could not be placed in a county (no usable ZIP, city or county). |
Gaps
A gap is a county in scope for which we hold no county-level source. It is listed with the live statewide sources that still cover the county (a state court index, a department of corrections roster, the exclusion lists) and any county-level source mapped for it and coming soon. A gap does not change the result; set requireCountyCoverage on the policy if your program needs it to.
The healthcare search in the plan
A healthcare search is not tied to the consumer’s counties: an exclusion in any state bars the person from that state’s program, so every healthcare source is searched for every consumer. The plan shows them in its own healthcare block — never mixed into jurisdictions, so asking for a healthcare search never widens where the criminal search looks.
| Field | What it holds |
|---|---|
scope | Always all_states. |
sources | Every healthcare source: id (the source code records carry), name, state, scope (federal or statewide), status (live or pending), recordKinds (exclusion, nurse-aide), live (whether a match is confirmed live at the source), identifiers (what its records carry), matchOn (the identifiers a match on it can rest on: full_dob, npi, license_number) and freshness (maxAgeDays, and when the publisher replaces the file). |
statesCovered | States with at least one live healthcare source. |
statesWithoutSource | States with none yet — disclosed, like a county gap. They do not change the result. |
sourceCounts | live and pending totals. |
A source we add to the healthcare package appears in the next plan, the next healthcare search and its coverage with no change on your side.
The live scope inside the search plan
The plan decides which sources an order covers; liveScope decides which of them are asked live. With relevant and all, the plan’s sources in the relevant states are searched live and the rest in our data. With matches, every source in the plan is searched in our stored copy first; a source in the plan that we hold no copy of is searched live (copyStatus: "none", liveReason: "no_copy" on its coverage row), and a possible record found anywhere in the plan is confirmed with the agency that keeps it before it can be reported. The plan is the same whichever live scope the order runs.
The identity layer
Every order carries identity. Identity verification (a government ID and selfie check) and address-history trace are coming soon; until a provider is live, both report not_available and the note says what the order was matched on: the name, the full date of birth and the addresses provided. An address source other than declared is refused until then.
The Identity object — verification and trace status
The identity layer of an order. Identity verification and address-history trace are coming soon; until a provider is live both report not_available and orders are matched on the name, full date of birth and the addresses provided.
Returned or accepted by POST /v1/fcra/orders, GET /v1/fcra/orders, GET /v1/fcra/orders/{order_id}, POST /v1/fcra/orders/{order_id}/cancel, GET /v1/fcra/orders/{order_id}/report, POST /v1/fcra/orders/{order_id}/adjudication.
| Field | Type | Description |
|---|---|---|
| verification | object | Document and selfie verification of the subject. |
| verification.status | string one of "not_available", "pending", "verified", "failed" | not_available until a provider is live. |
| verification.provider | string or null | The provider, once one is live. |
| trace | object | Address-history trace. |
| trace.status | string one of "not_available", "answered", "thin", "failed" | not_available until a provider is live. |
| trace.provider | string or null | The provider, once one is live. |
| note | string or null | A plain statement of what the order was matched on while the layer is not available. |
Test FCRA orders on the sandbox server
With a test key (os_test_…) every account is a sandbox FCRA partner on the sandbox server, with the same last-name rules. Register an end user, then place orders against it; nothing is billed. An order finishes right after the 202: read it once with GET /v1/fcra/orders/{orderId}.
- Base URL
https://sandbox.offendersearch.appNothttps://api.offendersearch.app, which takes live keys only.- Test key
- In the dashboard, Developers → API keys → Create test key. Test keys begin
os_test_; send yours in theX-API-Keyheader.
curl -X POST "https://sandbox.offendersearch.app/v1/fcra/end-users" \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Sample Staffing LLC",
"permissiblePurposes": [
"employment"
],
"certification": {
"text": "End user certifies it will use reports only for employment purposes.",
"version": "v1",
"certifiedAt": "2026-10-01T12:00:00Z"
}
}'curl -X POST "https://sandbox.offendersearch.app/v1/fcra/orders" \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"endUserId": "{endUserId}",
"permissiblePurpose": "employment",
"consent": {
"obtainedAt": "2026-10-01T12:00:00Z",
"method": "esign"
},
"subject": {
"firstName": "Pat",
"lastName": "Records",
"dob": "1980-01-01"
},
"searches": [
{
"type": "sex_offender"
},
{
"type": "criminal"
}
]
}'Try it here
Pick a scenario, edit the request, and run it against the sandbox from this page. The explorer accepts os_test_ keys only and never reaches the live host, so nothing here is billed; the “production” code it copies is for your own backend with a live key.
Copy this request as code
Sandbox code uses your test key. Production code calls the live host and is billed per call; run it from your backend with an os_live_… key.
curl -X POST https://sandbox.offendersearch.app/v1/fcra/end-users \
-H "X-API-Key: $OFFENDERSEARCH_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Sample Staffing LLC", "permissiblePurposes": [ "employment" ], "certification": { "text": "End user certifies it will use reports only for employment purposes.", "version": "v1", "certifiedAt": "2026-10-01T12:00:00Z" } }'Every test name: FCRA API sandbox ordersPostman collectionFCRA API sandbox