Filtering criminal searches
Filters, residence vs jurisdiction locations, chargeMatch, the counts that show what was removed, and five worked examples.
Livehttps://api.offendersearch.appSandboxhttps://sandbox.offendersearch.appCriminal search filters: narrow a named search
A criminal search always starts from a name. Add filters to keep only the records you care about (felonies, convictions, court cases, a date window, a custody status) and locations to say where the person lived or where their records are. Both are optional request keys on POST /v1/criminal/search; a search without them answers exactly as before, and a filtered search is billed like any other search.
Filters test the normalized fields every record carries, so the same request works in every jurisdiction, whatever words a court or jail uses. The source’s own words are still returned beside them.
counts.filteredOut, by the filter that removed it, and every record kept only because its value is unknown is counted in counts.unknownKept. If filtering is ever switched off, a filtered search is refused with 503 filters_unavailable; it is never answered unfiltered.How criminal record filters combine
Every filter you send must pass (AND). The values inside one list are alternatives (OR): "severity": ["felony", "misdemeanor"] keeps either. A list filter also takes a single string.
- Record filters test the whole record:
recordCategory,custodyStatus,hasPhoto,sources,excludeVacatedOrSealed, the date ranges (offenseDate,filedDate,dispositionDate, each tested against the record’s latest date of that kind) andlookbackYears(the latest of the three within that many years of today). - Charge filters test the record’s charges:
severity,outcome,convictedOnly,chargeCategories,statute(the start of the published citation) andchargeText(text anywhere in the published description).severityBasisnarrowsseverityto the bases you trust and does nothing on its own.convictedOnly: trueaddsconvictedto the outcomes a charge may have.
chargeMatch: any vs all
chargeMatch decides how charge filters meet. With "any" (the default) one charge must pass every charge filter, so “a felony conviction” means one charge that is both. With "all" each filter may be met by a different charge. Take this record, searched with "severity": ["felony"], "convictedOnly": true:
"cases": [ { "caseNumber": "2019-CF-0412", "charges": [
{ "description": "BURGLARY OF HABITATION", "disposition": "DISMISSED" }, // felony, dismissed
{ "description": "THEFT < $750", "disposition": "GUILTY" } // misdemeanor, convicted
] } ]| chargeMatch | Result |
|---|---|
| "any" (default) | Filtered out, counted under filteredOut.severity: no single charge is both a felony and a conviction. |
| "all" | Returned with matchesFilters: [{case: 0, charge: 0}, {case: 0, charge: 1}]: the felony is met by one charge and the conviction by the other. |
A record that passes a charge filter carries matchesFilters: the {case, charge} positions, in normalized.cases[].charges[], of the charges that met them. Every charge is still returned.
Criminal filter counts: filteredOut, unknownKept, quarantined
A search that sends filters or locations adds three counts and echoes what it applied:
counts.filteredOut: records removed, keyed by the filter that removed each one. A record is counted once, under the first filter it failed. Location misses arelocations, ornoResidencewhen a residence test had no published address to test. A record that fails the per-charge test is counted under the first charge filter you sent.counts.unknownKept: returned records that passed a filter only because their value is unknown, forseverity,outcome,custodyStatusand the date ranges.counts.quarantined: candidate records whose normalized values are withheld (normalized.quality: "quarantined"). Their own fields are returned as usual.appliedFilters: the filters as validated, withunknownandchargeMatchalways present.
Unknown values: keep or exclude
Courts and jails do not all publish everything. filters.unknown decides what severity, outcome, convictedOnly, custodyStatus and the date filters do with a value that is not known. "keep" (the default) returns the record and counts it in unknownKept, so a felony that a court never labelled is not lost. "exclude" removes it and counts it in filteredOut.
Records marked vacated, expunged or sealed are returned, marked normalized.vacatedOrSealed: true, unless you send excludeVacatedOrSealed: true.
Locations: residence vs jurisdiction in a criminal search
A person can live in one county and have records in another. Each entry in locations says which you mean with kind:
| kind | Matches | Notes |
|---|---|---|
| residence | Where the person lives: the published address (state, county, city, zipcode, or a radius). | A record with no published address cannot match; it is counted as noResidence. |
| jurisdiction | Where the record is: the state and county of the court, jail or agency. | city, zipcode and a radius are refused with 422 invalid_location. |
| any (default) | Either of the two. | city, zipcode and a radius test the residence only. |
- Up to 10 locations, ORed. A record is kept when it matches any of them, and lists the 0-based indexes of the ones it matched in
matchedLocations. - A radius applies to the residence only.
lat,lngandradiusMiles(0.1 to 100 miles) go together and are measured to the published address. A residence without coordinates cannot be tested and counts asnoResidence. - Locations define the candidates. Each location runs its own search: a
jurisdictionlocation searches its state and county, aresidencelocation searches its ZIP or city when it has one, andanyor a radius searches the name across every record. The candidates are pooled, then each is tested against every location. A name too broad to read in full is refused with422 query_too_broad, never answered in part. - One way to say where. Send
locationsor the flatstate,county,cityandzipcodequery keys, not both: both is422 ambiguous_location. A county matches with or without the word “County”.
Criminal search filter examples
Five common searches, each with its request and an abridged response.
1. A known person and two places they lived
You know the name and date of birth, a ZIP the person lived in, and that they also spent time in Oklahoma. One location tests the residence; the other matches either an Oklahoma address or an Oklahoma record.
curl -X POST https://api.offendersearch.app/v1/criminal/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "lastName": "Rivera", "firstName": "Jordan", "dob": "1988-04-12" },
"locations": [
{ "kind": "residence", "zipcode": "77060" },
{ "kind": "any", "state": "OK" }
]
}'{
"status": "complete",
"counts": { "records": 2, "recordsReturned": 2,
"sourcesQueried": 0, "sourcesComplete": 0, "sourcesIncomplete": 0,
"candidatesCapped": false, "exclusionsIncluded": false,
"filteredOut": { "locations": 3, "noResidence": 1 },
"unknownKept": {}, "quarantined": 0 },
"records": [
{ "externalId": "TX-HARRIS-DC:case:201944871",
"name": { "first": "JORDAN", "middle": "", "last": "RIVERA", "suffix": "" },
"matchState": "dob_match",
"address": { "type": "residence", "line1": "16966 NORTHCHASE DR APT 313",
"city": "HOUSTON", "county": "Harris", "state": "TX", "zipcode": "77060",
"lat": null, "lng": null },
"matchedLocations": [0] },
{ "externalId": "OK-TULSA:booking:2021-118342",
"name": { "first": "JORDAN", "middle": "", "last": "RIVERA", "suffix": "" },
"matchState": "dob_match",
"matchedLocations": [1] }
],
"appliedFilters": {
"locations": [ { "kind": "residence", "zipcode": "77060" },
{ "kind": "any", "state": "OK" } ],
"unknown": "keep", "chargeMatch": "any"
}
}- Each record says which location it matched in
matchedLocations(0 is the ZIP, 1 is Oklahoma). filteredOut.noResidence: 1is a candidate that matched neither location and had no published address to test against the ZIP. It was not dropped silently: it is counted.
2. Felony convictions in the last 7 years, anywhere
Charge filters test charges. With the default chargeMatch: "any", one charge must be both a felony and a conviction, and the record's latest offense, filing or disposition date must be within 7 years.
curl -X POST https://api.offendersearch.app/v1/criminal/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "lastName": "Rivera", "firstName": "Jordan", "dob": "1988-04-12" },
"filters": { "severity": ["felony"], "convictedOnly": true, "lookbackYears": 7 }
}'{
"status": "complete",
"counts": { "records": 2, "recordsReturned": 2,
"sourcesQueried": 0, "sourcesComplete": 0, "sourcesIncomplete": 0,
"candidatesCapped": false, "exclusionsIncluded": false,
"filteredOut": { "severity": 4, "lookbackYears": 1 },
"unknownKept": { "severity": 1 }, "quarantined": 0 },
"records": [
{ "externalId": "TX-HARRIS-DC:case:201944871",
"cases": [ { "caseNumber": "201944871",
"charges": [ { "statute": "PC 22.02", "description": "AGG ASSAULT W/DEADLY WEAPON",
"severity": "felony", "disposition": "GUILTY" } ] } ],
"normalized": {
"recordCategory": "court", "topSeverity": "felony",
"hasConviction": true, "convictionCount": 1,
"cases": [ { "stage": "closed", "caseClass": "felony",
"charges": [ { "category": "assault", "notACharge": false,
"severity": "felony", "severityBasis": "published",
"degree": null, "outcome": "convicted", "convicted": true,
"custodyEvent": null } ] } ] },
"matchesFilters": [ { "case": 0, "charge": 0 } ] }
],
"appliedFilters": { "severity": ["felony"], "convictedOnly": true, "lookbackYears": 7,
"unknown": "keep", "chargeMatch": "any" }
}matchesFilterspoints at the charge that met every charge filter, by its position innormalized.cases[].charges[]. Every charge on the record is still returned.unknownKept.severity: 1is a returned record whose severity could not be classified. It is kept becauseunknowndefaults tokeep; send"unknown": "exclude"to remove such records instead.
3. In custody or wanted, within 25 miles of Dallas
A radius tests where the person lives, so kind: "any" with a radius matches on the residence only. A location with no ZIP or city searches the name across every record, so a very common name can be refused with 422 query_too_broad; add a first name or date of birth.
curl -X POST https://api.offendersearch.app/v1/criminal/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "lastName": "Rivera", "firstName": "Jordan" },
"locations": [ { "kind": "any", "lat": 32.78, "lng": -96.80, "radiusMiles": 25 } ],
"filters": { "custodyStatus": ["in_custody", "wanted"] }
}'{
"status": "complete",
"counts": { "records": 1, "recordsReturned": 1,
"sourcesQueried": 0, "sourcesComplete": 0, "sourcesIncomplete": 0,
"candidatesCapped": false, "exclusionsIncluded": false,
"filteredOut": { "noResidence": 6, "locations": 2, "custodyStatus": 3 },
"unknownKept": {}, "quarantined": 0 },
"records": [
{ "externalId": "TX-DALLAS:booking:26-0041177",
"currentStatus": "in_custody",
"normalized": { "recordCategory": "jail", "custodyStatus": "in_custody",
"residence": { "city": "GARLAND", "state": "TX", "zip5": "75040",
"lat": 32.93, "lng": -96.62, "geoPrecision": "street" } },
"matchedLocations": [0] }
],
"appliedFilters": {
"custodyStatus": ["in_custody", "wanted"],
"locations": [ { "kind": "any", "lat": 32.78, "lng": -96.8, "radiusMiles": 25.0 } ],
"unknown": "keep", "chargeMatch": "any"
}
}noResidence: 6are candidates with no published address, or an address without coordinates, so the radius could not be tested. They are counted, not hidden.appliedFiltersechoes each location with the keys it was read as.
4. Violent or weapons charges, with a published severity only
severityBasis works together with severity: list every severity you accept and the bases you trust. "unknown": "exclude" removes charges and records whose severity could not be decided at all.
curl -X POST https://api.offendersearch.app/v1/criminal/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "lastName": "Rivera", "firstName": "Jordan", "dob": "1988-04-12" },
"filters": {
"chargeCategories": ["assault", "weapons"],
"severity": ["felony", "misdemeanor", "infraction"],
"severityBasis": ["published"],
"unknown": "exclude"
}
}'{
"status": "complete",
"counts": { "records": 1, "recordsReturned": 1,
"sourcesQueried": 0, "sourcesComplete": 0, "sourcesIncomplete": 0,
"candidatesCapped": false, "exclusionsIncluded": false,
"filteredOut": { "chargeCategories": 2, "severity": 2 },
"unknownKept": {}, "quarantined": 0 },
"records": [
{ "externalId": "TX-HARRIS-DC:case:201944871",
"matchesFilters": [ { "case": 0, "charge": 0 } ] }
],
"appliedFilters": {
"severity": ["felony", "misdemeanor", "infraction"], "severityBasis": ["published"],
"chargeCategories": ["assault", "weapons"],
"unknown": "exclude", "chargeMatch": "any"
}
}- With
chargeMatch: "any", one charge must be an assault or weapons charge and carry a severity the source published itself. severityBasismust be sent withseverity; on its own it is refused with a 422.
5. Convicted court records, vacated or sealed left out
A screening-style search: court records only, a conviction on at least one charge, and records marked vacated or sealed left out by choice. include: ["facets"] adds counts over the whole result.
curl -X POST https://api.offendersearch.app/v1/criminal/search \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": { "lastName": "Rivera", "firstName": "Jordan", "dob": "1988-04-12" },
"filters": { "recordCategory": ["court"], "outcome": ["convicted"],
"excludeVacatedOrSealed": true },
"include": ["facets"]
}'{
"status": "complete",
"counts": { "records": 3, "recordsReturned": 3,
"sourcesQueried": 0, "sourcesComplete": 0, "sourcesIncomplete": 0,
"candidatesCapped": false, "exclusionsIncluded": false,
"filteredOut": { "recordCategory": 5, "excludeVacatedOrSealed": 1, "outcome": 2 },
"unknownKept": { "outcome": 1 }, "quarantined": 0 },
"records": [ "…" ],
"facets": {
"recordCategory": { "court": 3 },
"severity": { "felony": 1, "misdemeanor": 1, "unknown": 1 },
"outcome": { "convicted": 2, "dismissed": 1, "unknown": 1 },
"jurisdiction.state": { "TX": 2, "OK": 1 },
"chargeCategories": { "assault": 1, "theft": 1, "traffic": 1 },
"custodyStatus": { "disposed": 2, "unknown": 1 }
},
"appliedFilters": { "recordCategory": ["court"], "outcome": ["convicted"],
"excludeVacatedOrSealed": true, "unknown": "keep", "chargeMatch": "any" }
}facetscounts every record that passed, not only this page. A record with several outcomes or charge categories counts once under each.- Without
excludeVacatedOrSealed, a record marked vacated or sealed is returned withnormalized.vacatedOrSealed: true.
Sorting and facets for filtered criminal searches
sort orders a search that sends filters or locations: relevance (the default), or offenseDate, filedDate or dispositionDate, newest first by the record’s latest date of that kind, with records that have none last.
include: ["facets"] adds facets: counts of every record that passed (not only the page) by record category, highest severity, outcome, state, charge category and custody status. include: ["normalizedDetail"] adds the rule that decided each charge’s outcome, for audits.
Filters work on cached searches and on live checks of up to 2 sources. A live search that has to run as a job refuses them with 422 filters_with_async_live.
Criminal filter errors
Every filter and location is checked before the search runs. A bad value is a 422 in the criminal error shape, with a message that names the key.
| error.code | When |
|---|---|
| invalid_filter_value | A value outside a filter's list, a wrong type, an unknown filter key, a statute or chargeText under 2 characters, a lookbackYears outside 1–100, or a bad sort, unknown or chargeMatch. The message lists the allowed values. |
| invalid_date_range | A date range with neither from nor to, a value that is not YYYY-MM-DD or not a real date, or from after to. |
| invalid_location | An empty locations list; an unknown key, a bad state or ZIP, or no place in a location; lat, lng and radiusMiles not sent together or out of range; a radius below 0.1; a radius, city or zipcode on kind: "jurisdiction". |
| too_many_locations | More than 10 locations. |
| ambiguous_location | locations sent together with the flat state, county, jurisdiction, city, zipcode or address query keys. |
| radius_too_large | radiusMiles above 100. |
| filters_with_async_live | filters or locations on a live search that has to run as a job. Filters work on cached searches and on live checks of up to 2 sources. |
| filters_unavailable (503) | Filtering is switched off for the moment. A filtered search is never answered unfiltered; retry after Retry-After seconds, or search without filters. |
The SearchFilters object — criminal search filter fields
The filters of a criminal search. Every key is optional; filters are ANDed and the values inside one list are ORed. A list filter also takes a single string. Any other key is 422 invalid_filter_value.
Returned or accepted by POST /v1/criminal/search.
| Field | Type | Description |
|---|---|---|
| recordCategory optional | array of string one of "court", "jail", "prison", "warrant", "registry", "exclusion", "other" | The kind of record (normalized.recordCategory). |
| severity optional | array of string one of "felony", "misdemeanor", "infraction" | Charge severity. With unknown: "keep" an unclassified charge also passes. |
| severityBasis optional | array of string one of "published", "statute_code", "statute_table", "case_type", "charge_text" | Narrows severity: a charge's severity counts only when it was decided on one of these bases. Must be sent with severity; on its own it is a 422 invalid_filter_value. |
| outcome optional | array of string one of "convicted", "pending", "dismissed", "nolle_prosequi", "acquitted", "deferred_or_diverted", "vacated", "warrant", "transferred", "closed_unspecified", "administrative" | Charge outcome class. With unknown: "keep" a charge whose outcome is unknown also passes. |
| convictedOnly optional · default false | boolean | true adds convicted to the outcomes a charge may have. Alone it means a convicted charge; with outcome the charge may have any of those outcomes or convicted. |
| chargeCategories optional | array of string one of "supervision", "homicide", "sex_offense", "robbery", "domestic_violence", "dui", "assault", "weapons", "burglary", "fraud", "theft", "drug_distribution", "drug_possession", "traffic", "public_order", "other" | What the charge is about. |
| chargeMatch optional · default "any" | string one of "any", "all" | any: one charge must meet every charge filter. all: each charge filter may be met by a different charge on the record. |
| statute optional | string | The start of the published statute citation, ignoring spaces and case: 22.02 matches 22.02(a)(1). At least 2 characters.Example "22.02" |
| chargeText optional | string | Text that appears anywhere in the published charge description, ignoring case. At least 2 characters. Example "assault" |
| custodyStatus optional | array of string one of "in_custody", "released", "on_parole", "on_probation", "active_warrant", "wanted", "registered", "disposed", "absconder", "deceased", "excluded" | The record's custody status. With unknown: "keep" a record whose status is unknown also passes. |
| offenseDate optional | DateRange | The record's latest offense date must fall in this range. |
| filedDate optional | DateRange | The record's latest filing date must fall in this range. |
| dispositionDate optional | DateRange | The record's latest disposition date must fall in this range. |
| lookbackYears optional | integer | The latest of the record's offense, filing and disposition dates must be within this many years of today. A whole number from 1 to 100. Example 7 |
| hasPhoto optional · default false | boolean | true keeps only records with a published photograph. |
| sources optional | array of string | Keep only records from these source codes. Example ["TX-HARRIS-DC"] |
| excludeVacatedOrSealed optional · default false | boolean | true leaves out records marked normalized.vacatedOrSealed. By default they are returned, marked. |
| unknown optional · default "keep" | string one of "keep", "exclude" | What severity, outcome, convictedOnly, custodyStatus and the date filters do with a value that is not known. keep returns the record and counts it in counts.unknownKept; exclude removes it and counts it in counts.filteredOut. |
The DateRange object — date filter fields
An inclusive ISO date range. Send from, to or both; from after to, a non-ISO value or an impossible date is 422 invalid_date_range.
Returned or accepted by POST /v1/criminal/search.
| Field | Type | Description |
|---|---|---|
| from optional | string (date) | First date in the range. Example "2019-01-01" |
| to optional | string (date) | Last date in the range. Example "2026-10-23" |
The Location object — residence and jurisdiction fields
One place in locations[]. It needs at least one of state, county, city, zipcode, or a radius (lat, lng and radiusMiles together). Any other key is 422 invalid_location.
Returned or accepted by POST /v1/criminal/search.
| Field | Type | Description |
|---|---|---|
| kind optional · default "any" | string one of "residence", "jurisdiction", "any" | residence matches where the person lives (the published address). jurisdiction matches where the record is (the court, jail or agency's state and county). any matches either. A radius, city and zipcode describe a residence: on jurisdiction they are 422 invalid_location, and on any they test the residence only. |
| state optional | string | Two-letter state code. Example "TX" |
| county optional | string | County name; Harris and Harris County match the same county.Example "Harris" |
| city optional | string | City of the residence, matched exactly, ignoring case. Example "Houston" |
| zipcode optional | string | Five-digit ZIP of the residence. A ZIP+4 is read as its first five digits; zip is accepted as another name for this key.Example "77060" |
| lat optional | number | Latitude of the radius centre, -90 to 90. Requires lng and radiusMiles.Example 32.78 |
| lng optional | number | Longitude of the radius centre, -180 to 180. Requires lat and radiusMiles.Example -96.8 |
| radiusMiles optional | number | Miles from lat/lng to the person's published residence, 0.1 to 100. Above 100 is 422 radius_too_large; below 0.1 is 422 invalid_location. A residence without coordinates cannot be tested and counts as noResidence.Example 25 |
The SearchFacets object — facet counts on a filtered search
Counts over every record that passed the filters: the whole result, not the page. Each key maps a value to a number of records.
Returned or accepted by POST /v1/criminal/search, GET /v1/criminal/searches/{searchId}.
| Field | Type | Description |
|---|---|---|
| recordCategory | object | Records by normalized.recordCategory.Example {"court":9,"jail":5} |
| severity | object | Records by their highest charge severity; unknown when none is classified.Example {"felony":2,"misdemeanor":9,"unknown":3} |
| outcome | object | Records by outcome class; a record with several outcomes counts once under each. Example {"convicted":6,"dismissed":4,"pending":4} |
| jurisdiction.state | object | Records by the state where the record is; the 60 largest. Example {"TX":12,"OK":2} |
| chargeCategories | object | Records by charge category; a record with several categories counts once under each. Example {"traffic":7,"theft":3,"assault":2} |
| custodyStatus | object | Records by normalized.custodyStatus.Example {"released":10,"in_custody":4} |