API reference
One REST endpoint enrolls and rescreens a person or company. Another withdraws them. Base URL https://onsanctions.com.
Quickstart
- Create an organization and choose a plan on pricing. To build first, skip the plan and use a test key.
- Create an API key on the account page. It is shown once, so store it as a secret.
- Send a person or company to
POST /v1/screen. The first call enrolls and screens them. Sending the same subject again rescreens them. - When the relationship ends, send them to
POST /v1/withdrawso they stop counting toward your plan.
The example on the right is fictional. Copy it, set ONSANCTIONS_API_KEY, and run it.
curl -X POST https://onsanctions.com/v1/screen \
-H "x-api-key: $ONSANCTIONS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "name": "SAMPLE MATCH PERSON", "entity": 0, "country": "GB", "dataset": "sanctions", "identifierType": "passport", "identifierValue": "SAMPLE12345", "identifierCountry": "GB"}'Authentication
Send your key in the x-api-key header on every request. Keys are secrets: call the API from your server, never from a browser or app. You can keep up to five active keys, restrict each to IP addresses, and revoke them on the account page.
Idempotency and limits
Every request needs an Idempotency-Key of 8 to 160 characters. Repeating a key with the same body returns the stored response for 48 hours without screening again, so a retry after a timeout can never enroll or charge twice. Reusing a key with a different body returns 409.
Each organization can make 60 requests a minute. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-Request-Id.
Test keys
Choose Test when you create a key. A test key calls the same endpoints with the same body and needs no plan, but every result is a mock. Never use it for a compliance decision.
- Nothing is enrolled with the screening partner, and there is no onboarding fee.
- Test subjects are kept apart from live ones and don't count toward your plan.
- Responses carry
"mode": "test", andoperationbehaves as it would live. - An organization can make 100 test calls a day and keep 100 test subjects enrolled.
Screen a subject
POST/v1/screen
Enrolls a person or company and screens them. Send the same subject later to rescreen: there is nothing else to call. We never store the name or the identifier, only a keyed hash of the identifier.
The first screen of a subject carries a one-time onboarding fee. Rescreens are included in your plan. A clear result means no possible match in the categories and dataset version on that response. It is not a clearance.
Headers
- x-api-keystringrequired
- Your API key from the account page. Live keys start with os_live_, test keys with os_test_.
- Idempotency-Keystringrequired
- 8 to 160 characters, unique per operation. A retry with the same key never screens or enrolls twice.
- Content-Typestringrequired
- application/json
Body
- namestringrequired
- Full name, or the registered company name. 2 to 300 characters.
- entityintegerrequired
- 0 person, 1 company, 2 organization, 3 legal entity.
- countrystringrequired
- 2-letter ISO code: nationality for a person, country of registration for a company.
- datasetstringrequired
- all, sanctions, peps or crime. PEP and RCA are included on this license. PEP enrichment includes Wikidata alongside official publisher feeds.
- identifierTypestringrequired
- passport for a person (entity 0), lei for everything else.
- identifierValuestringrequired
- A passport number of 5 to 20 letters or digits, or a 20-character LEI.
- identifierCountrystringrequired
- 2-letter ISO code of the country that issued the identifier.
Response
- operationstring
- onboarding on the first screen of a subject, ongoing on every rescreen.
- results.Header.DatasetVersionstring
- The dataset version this screen ran against. Keep it as your audit evidence.
- results.SearchResults.Foundboolean
- true when there is at least one possible match.
- results.PossibleMatches.Resultsarray
- The possible matches, with their list and category, for your review.
- modestring
- live, or test for a test key (mock results).
- requestIdstring
- Our id for this call. Quote it when you contact support.
A screen that is still running returns 202 with Retry-After. Retry with the same Idempotency-Key.
{
"name": "SAMPLE MATCH PERSON",
"entity": 0,
"country": "GB",
"dataset": "sanctions",
"identifierType": "passport",
"identifierValue": "SAMPLE12345",
"identifierCountry": "GB"
}{
"success": true,
"operation": "onboarding",
"screened_name": "SAMPLE MATCH PERSON",
"screened_entity_type": 0,
"screened_country": "gb",
"screened_dataset": "sanctions",
"results": {
"Success": true,
"ResponseCode": 200,
"Header": {
"DatasetVersion": "aml_20260817_sample",
"DatasetPublishedAt": "2026-08-17T07:12:00.000Z",
"ScreenedAt": "2026-08-17T08:00:00.000Z",
"CategoriesRequested": ["sanctions"]
},
"SearchResults": {
"Found": true,
"Message": "Possible match",
"RecordCount": 1
},
"PossibleMatches": {
"Found": true,
"Results": [{ "Name": "SAMPLE MATCH PERSON", "Category": "Sanctions" }]
}
},
"mode": "live",
"requestId": "6f1c2a9e-0d4b-4e8a-9a51-2b7f4c1d3e88"
}Withdraw a subject
POST/v1/withdraw
Removes a subject from your active population when the relationship ends. Send the same entity and identifier you enrolled them with. Name, country and dataset are not needed. A subject that isn't enrolled returns 404.
Headers
- x-api-keystringrequired
- Your API key from the account page. Live keys start with os_live_, test keys with os_test_.
- Idempotency-Keystringrequired
- 8 to 160 characters, unique per operation. A retry with the same key never screens or enrolls twice.
- Content-Typestringrequired
- application/json
Body
- entityintegerrequired
- 0 person, 1 company, 2 organization, 3 legal entity.
- identifierTypestringrequired
- passport for a person (entity 0), lei for everything else.
- identifierValuestringrequired
- A passport number of 5 to 20 letters or digits, or a 20-character LEI.
- identifierCountrystringrequired
- 2-letter ISO code of the country that issued the identifier.
curl -X POST https://onsanctions.com/v1/withdraw \
-H "x-api-key: $ONSANCTIONS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "entity": 0, "identifierType": "passport", "identifierValue": "SAMPLE12345", "identifierCountry": "GB"}'{
"entity": 0,
"identifierType": "passport",
"identifierValue": "SAMPLE12345",
"identifierCountry": "GB"
}{
"success": true,
"withdrawn": true,
"mode": "live",
"requestId": "0b9e3c51-7a2d-4f6e-8c14-5d2a9f7e1b03"
}List-change notifications
When a list changes or a source falls behind, we send a signed POST to the HTTPS URL you save on the account page. It tells you to rescreen. It doesn't contain results, and subjects are not rescreened for you.
UN, OFAC, UK, and EU lists are scheduled every 60 minutes. A check lands when the publisher responds. Most other configured sources are scheduled every six hours. Publisher availability and backoff decide whether a cycle lands.
Headers we send
- x-onsanctions-event-idstring
- Unique per event. Ignore ids you have already handled.
- x-onsanctions-timestampstring
- Unix seconds. Reject events older than five minutes.
- x-onsanctions-signaturestring
- v1= and the hex HMAC-SHA256 of the timestamp, a period and the raw body, keyed with your signing secret.
Event types: aml_monitoring.dataset_updated and aml_monitoring.source_stale.
{
"id": "evt_sample",
"type": "aml_monitoring.dataset_updated",
"createdAt": "2026-08-17T07:12:00.000Z",
"data": {
"datasetVersion": "aml_20260817_sample",
"publishedAt": "2026-08-17T07:12:00.000Z"
}
}import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, headers, secret) {
const timestamp = headers["x-onsanctions-timestamp"];
const signature = headers["x-onsanctions-signature"] ?? "";
const expected = "v1=" + createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && signature.length === expected.length &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}MCP server
POST/mcp
AI assistants and agents can screen through our Model Context Protocol server. Connect with the same API key in the x-api-key header. The tools are screen and withdraw, with the same fields as the REST endpoints. Use a test key while you try it.
{
"mcpServers": {
"onsanctions": {
"url": "https://onsanctions.com/mcp",
"headers": { "x-api-key": "os_test_..." }
}
}
}Errors
Errors return JSON with an error, sometimes a code and message, and the requestId.
| Status | When | What to do |
|---|---|---|
| 202pending | The screen is still running. | Retry with the same Idempotency-Key after Retry-After seconds. |
| 400 | The body, identifier or Idempotency-Key is not valid. | Fix the request. The message says which part. |
| 401 | The API key is unknown or revoked. | Check the key, or create a new one on the account page. |
| 402 | A live key's subscription is not active, or access is paused. | Subscribe, or update billing on the account page. |
| 403POPULATION_LIMIT_EXCEEDED | A new enrollment would pass your plan's subject limit (100 for test keys). | Withdraw subjects you no longer serve, or move to a larger band. |
| 403 | The key's IP allowlist rejected the caller. | Call from an allowed address, or edit the key's allowlist. |
| 404 | A withdrawal names a subject that is not enrolled. | Nothing to do: the subject is already out of your population. |
| 409 | The Idempotency-Key was reused with a different body. | Use a new key for a different request. |
| 429RATE_LIMITED | Over 60 requests a minute for this organization. | Wait for Retry-After, then retry. |
| 429TEST_LIMIT_EXCEEDED | Test keys are over 100 calls a day. | Try tomorrow, or use a live key. |
| 502, 503, 504 | Screening is temporarily unavailable. | Retry with the same Idempotency-Key. It is safe. |
{
"error": "POPULATION_LIMIT_EXCEEDED",
"message": "Your licensed population is full. Withdraw subjects or move to a larger band.",
"requestId": "c4a8e2f1-3b6d-4c9a-8e71-0f5d2b9a6c34"
}