Batch & CSV search
Up to 1000 lookups per call, JSON or CSV, row in and row out, with per-row fault isolation.
Base URL https://api.offendersearch.appThis page as Markdown/docs/batch.mdOne row in, one result out
The batch endpoint is the shape to reach for when you already hold a list: a roster to screen, a CSV export to reconcile, a nightly re-check of a population. Each row runs through the same engine as POST /v1/search, so every result carries the same envelope — counts, sourceStatus, labelled matchState — and the same Record schema.
- Three accepted body shapes. A bare JSON array of
Queryobjects, a JSON envelope{ "queries": [...], ...options }whose non-querieskeys apply to every row, or atext/csvbody whose header row names Query fields. - Input order is preserved. Each result carries its 0-based
index, so you can zip results back onto your own rows without matching on identity. - Rows are fault-isolated. A row that fails is reported with
status: "error"in place and never aborts the rest of the batch. - 1000 rows per call. A larger batch returns
413.
daily (the default). Set freshness: "weekly" on a row, or batch-wide, to meter that search with no surcharge.Batch / CSV search
Run many lookups in one call — row in, row out. Send a JSON array of queries, a JSON envelope with batch-wide options, or a text/csv body. Each row runs through the same engine as /v1/search and results come back in input order.
The batch endpoint provides legacy CSV-batch parity: one row = one call in, one result out. Post a JSON array of Query objects, a JSON envelope { "queries": [...], ...options } where the non-queries keys (jurisdictions, freshness, match, recordTypes, locationScoped, include) apply to every row, or a text/csv body whose header row names Query fields (firstName,lastName,state,dob,city,zipcode,address,age,q).
Rows run with bounded server-side concurrency and results return in the same order you sent them. A row that fails is reported with status: "error" in place — it never aborts the rest of the batch. The maximum is 1000 rows per call (a larger batch returns 413).
Billing is per search, not per request. A batch is one HTTP call but each search in it is billed as one search — a batch of 100 rows = 100 metered searches, each at the graduated per-call rate plus its own +$0.01 daily-freshness surcharge when that row is daily (the default). Set freshness: "weekly" on a row (or batch-wide) to bill that search with no surcharge.
Every row is served at the freshness tier it asks for. weekly is the natural fit here — bulk and periodic re-screening work is exactly what it is for, and it carries no surcharge; set daily on the rows where currency decides the outcome. For very large or long-running workloads, the asynchronous endpoint is the other batch-shaped option: submit and collect, with no request-timeout ceiling.
- JSON or CSV. Post a JSON array, a JSON envelope with batch-wide options, or a text/csv body — whatever your pipeline already produces.
- Row-in / row-out. Results return in input order, each tagged with its 0-based index.
- Fault-isolated. A row that errors is reported with status:"error" in place and never aborts the batch.
- Up to 1000 rows. One call handles up to 1000 queries; larger batches return 413.
Send one of: a JSON array of Query objects, a JSON envelope, or a text/csv body.
| Field | Type | Description |
|---|---|---|
| queries required | Query[] | The per-row queries (envelope form). Or post a bare JSON array, or a CSV body. |
| jurisdictions / freshness / match / recordTypes / locationScoped / include optional | various | Batch-wide options in the envelope form — applied to every row. |
# JSON envelope: batch-wide options apply to every row
curl https://api.offendersearch.app/v1/batch \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: application/json" \
-d '{
"queries": [
{ "firstName": "John", "lastName": "Smith", "state": "FL" },
{ "firstName": "Jane", "lastName": "Doe", "state": "TX" }
],
"freshness": "daily",
"match": "balanced"
}'
# …or upload a CSV whose header row names Query fields
curl https://api.offendersearch.app/v1/batch \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: text/csv" \
--data-binary $'firstName,lastName,state\nJohn,Smith,FL\nJane,Doe,TX'{
"count": 2,
"results": [
{ "index": 0, "status": "complete",
"counts": { "records": 1, "sourcesQueried": 3 },
"records": [ /* … scored, de-duplicated records … */ ] },
{ "index": 1, "status": "complete",
"counts": { "records": 0, "sourcesQueried": 3 }, "records": [] }
]
}Results are in input order. A failed row is { "index": n, "status": "error", "error": "…" } in place. Returns 413 if the batch exceeds 1000 rows.
The CSV form
Post a text/csv body whose header row names Query fields — firstName, lastName, state, dob, city, zipcode, address, age, q. Every row is a query, and the response is the same JSON envelope as the JSON form, so a pipeline that already produces a CSV needs no transformation step.
curl https://api.offendersearch.app/v1/batch \
-H "X-API-Key: $OFFENDERSEARCH_KEY" \
-H "Content-Type: text/csv" \
--data-binary @roster.csv
# roster.csv
# firstName,lastName,state,dob
# John,Smith,FL,1980-04-12
# Jane,Doe,TX,{
"count": 2,
"results": [
{ "index": 0, "status": "complete",
"counts": { "records": 1, "sourcesQueried": 58,
"sourcesComplete": 58, "sourcesIncomplete": 0 },
"records": [ /* … */ ] },
{ "index": 1, "status": "partial",
"counts": { "records": 0, "sourcesQueried": 58,
"sourcesComplete": 57, "sourcesIncomplete": 1 },
"records": [] }
]
}Batch or async?
Both are volume shapes and they compose. Batch is synchronous and row-oriented: you hold the connection, and you get every row back in one response, in order. Async (POST /v1/searches) is one search submitted and collected later, with no request-timeout ceiling — the right shape for a single very broad search, or when you would rather be called back than wait.
| Batch | Async | |
|---|---|---|
| Unit | Many queries, one call | One query, collected later |
| Delivery | In the response, in input order | Poll or signed webhook |
| Ceiling | 1000 rows per call | No request-timeout ceiling |
| Use it for | Rosters, CSV reconciliation, scheduled re-screens | Very broad single searches, unattended jobs |