Endpoint & error reference
All five endpoints in one place — create, list, retrieve, cancel, alerts — and the error shape.
Livehttps://api.offendersearch.appSandboxhttps://sandbox.offendersearch.appThe five endpoints
Every request authenticates with the X-API-Key header. Base URL https://api.offendersearch.app. Each endpoint below has a Request table and a Response table listing every field; objects that live on their own page — The Monitor object and the alert object — are linked, not repeated.
| Method | Path | What it does |
|---|---|---|
| POST | /v1/monitors | Create a monitor. |
| GET | /v1/monitors | List monitors. |
| GET | /v1/monitors/{monitorId} | Retrieve a monitor. |
| DELETE | /v1/monitors/{monitorId} | Cancel a monitor. |
| GET | /v1/monitors/{monitorId}/alerts | Alert history. |
POST /v1/monitors — create monitor request and response fields
Create a person or location monitor on the sex offender or criminal dataset. Returns the new monitor with 201.
Request
| Field | Type | Description |
|---|---|---|
| product required | string one of "sex-offender", "criminal" | The dataset this monitor watches. One product per monitor: watching one person in both datasets is two monitors, billed separately. Example "sex-offender" |
| type required | string one of "person", "location" | The predicate. person watches a name (and date of birth); location watches a radius around a point. criminal with location returns 200 not_yet_available and creates nothing.Example "person" |
| person optional | PersonCriteria or null | Required when type is person; ignored otherwise. |
| location optional | LocationCriteria or null | Required when type is location; ignored otherwise. |
| minConfidence optional | string or null one of "dob_match", "year_match", "age_match", "name_match", "any" | The weakest match strength a person monitor alerts on, strongest first: dob_match, year_match, age_match, name_match. Unset or any applies the default floor: year_match when person.dob is set, name_match when it is not. Location monitors alert on every record in the radius and do not read this field. Any other value is a 422.Example "dob_match" |
| label optional | string or null | Your own name for the monitor. Echoed back on the monitor and in the alert email and webhook (monitorLabel).Example "Jordan Rivera — SO watch" |
| channels optional | Channels | Where alerts go. Send at least one email address or a webhookUrl, or the request is a 422. |
Response
The monitor was created and is active.
The body is the Monitor object. One standing watch, as POST /v1/monitors, GET /v1/monitors and GET /v1/monitors/{monitorId} return it. A person monitor carries person and no location key; a location monitor carries location and no person key — read type, then the matching object.
product: "criminal" with type: "location". No monitor was created and nothing is billed; branch on status.
| Field | Type | Description |
|---|---|---|
| status | string one of "not_yet_available" | Always not_yet_available. |
| message | string | A sentence you can show a user. |
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key header (or dashboard session token). Body: the Error object, detail a sentence. |
| 402 | — | The account has no active subscription and no active free trial. |
| 422 | — | Validation failed. detail is a sentence naming the field for an unknown product, type or minConfidence, no channel, a person monitor without person.lastName, a person.dob that is not YYYY-MM-DD, a location monitor without location.address or lat+lng, or radiusMiles outside (0, 100]. A missing product or type, or a wrongly typed field, returns detail as a list of {type, loc, msg} objects. |
curl -X POST "https://api.offendersearch.app/v1/monitors" \
-H "X-API-Key: $OFFENDERSEARCH_KEY" -H "Content-Type: application/json" \
-d '{ "product": "sex-offender", "type": "person",
"person": { "firstName": "Jordan", "lastName": "Rivera", "dob": "1988-04-12" },
"channels": { "email": "alerts@example.com" } }'POST for product: "criminal" with type: "location" returns a 200 with status: "not_yet_available" — no monitor is created and nothing is billed. See Location monitoring.GET /v1/monitors — list monitors request and response fields
Every monitor on the account, newest first. Active monitors only unless you pass includeCanceled=true.
Request
| Field | Type | Description |
|---|---|---|
| includeCanceled optional · default false | boolean | true also returns canceled monitors. |
Response
The monitors.
| Field | Type | Description |
|---|---|---|
| monitors | array of Monitor | The account's monitors, newest createdAt first. [] when there are none. |
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key header (or dashboard session token). Body: the Error object, detail a sentence. |
| 402 | — | The account has no active subscription and no active free trial. |
{
"monitors": [
{ "id": "mon_b8f40d7afb9dc0d8", "product": "sex-offender", "type": "person",
"minConfidence": null, "label": "Jordan Rivera — SO watch", "status": "active",
"channels": { "emails": ["alerts@example.com"] },
"createdAt": "2026-09-05T14:02:11+00:00", "canceledAt": null,
"person": { "firstName": "Jordan", "lastName": "Rivera", "dob": "1988-04-12" } }
]
}GET /v1/monitors/{id} and /alerts
GET /v1/monitors/{id} returns one monitor with its ten most recently observed alerts in recentAlerts; GET /v1/monitors/{id}/alerts returns its alert history, up to 200 alerts per call (limit, default 50). Both are scoped to the creating account; another account’s id returns 404. The fields are defined on The Monitor object and Alerts.
GET /v1/monitors/{id} — retrieve monitor request and response fields
One monitor with recentAlerts, its ten most recently observed alerts. Works on active and canceled monitors.
Request
| Field | Type | Description |
|---|---|---|
| monitorId required | string | The monitor id (mon_…). Another account's monitor id is a 404.Example "mon_b8f40d7afb9dc0d8" |
Response
The monitor, including recentAlerts.
The body is the Monitor object. One standing watch, as POST /v1/monitors, GET /v1/monitors and GET /v1/monitors/{monitorId} return it. A person monitor carries person and no location key; a location monitor carries location and no person key — read type, then the matching object.
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key header (or dashboard session token). Body: the Error object, detail a sentence. |
| 402 | — | The account has no active subscription and no active free trial. |
| 404 | — | No monitor with this id on your account: {"detail": "Monitor not found."}. |
DELETE /v1/monitors/{id} — cancel monitor request and response fields
Cancel a monitor. It stops being evaluated at once and is not charged on any invoice prepared after the cancel. The monitor and its alerts stay readable with GET /v1/monitors/{monitorId}.
Request
| Field | Type | Description |
|---|---|---|
| monitorId required | string | The monitor id (mon_…). Another account's monitor id is a 404.Example "mon_b8f40d7afb9dc0d8" |
Response
The monitor is now canceled.
| Field | Type | Description |
|---|---|---|
| status | string one of "canceled" | Always canceled. |
| id | string | The canceled monitor's id. |
| note | string | A fixed sentence about when billing ends. Evaluation stops as soon as the monitor is canceled. |
| Status | Code | When |
|---|---|---|
| 401 | — | No valid X-API-Key header (or dashboard session token). Body: the Error object, detail a sentence. |
| 402 | — | The account has no active subscription and no active free trial. |
| 404 | — | No monitor with this id on your account: {"detail": "Monitor not found."}. |
| 409 | personal_alert | The monitor is a personal alert paid by its own subscription; it is canceled from Alerts in the personal dashboard. detail is {code, message}. |
A canceled monitor stops being evaluated at once and stays readable, so its alert history is preserved. See Billing.
{
"status": "canceled",
"id": "mon_b8f40d7afb9dc0d8",
"note": "Monitoring stops and billing ends at the close of the current cycle."
}The error envelope
Every monitoring error is a JSON object with one key, detail. For most errors detail is a sentence you can show a user. A body that fails type validation (a missing product, a string where a number belongs) returns detail as a list of { type, loc, msg } objects, where loc is the path to the field. Branch on the HTTP status; read detail for the reason. The shape is the Error object shared by every OffenderSearch API.
{
"detail": "channels must include at least one email or a webhookUrl."
}{
"detail": [
{ "type": "missing", "loc": ["body", "product"], "msg": "Field required",
"input": { "type": "person" } }
]
}| HTTP | When you’ll see it |
|---|---|
| 401 | No valid X-API-Key header (or dashboard session token). |
| 402 | The account has no active subscription and no active free trial. |
| 404 | No monitor with that id on your account — including another account’s monitor id. |
| 409 | DELETE on a personal alert, which is billed by its own subscription. detail is { code: "personal_alert", message }. |
| 422 | The create body failed validation. detail is a sentence naming the field, or a list of { type, loc, msg } objects for a missing or wrongly typed field. |
The monitoring endpoints do not return 429. Apply your own client-side concurrency control.