Integration checklist
What to decide, build and test before Crown Intelligence screening goes live in your system: the collection, the field mapping, the match threshold, how possible matches are reviewed and logged, how records stay current, and how the integration handles limits and errors.
The screening API needs no key today and answers anonymous requests. The checks below use the endpoints in the Screening API guide and the API reference. Automated or systematic screening, and use in a business process, need a commercial licence under our terms of use. Confirm that before you connect a live system.
Choose a collection
Each collection is a separate match path. Pick the one that matches the lists your obligations cover. A register is not a collection on its own; to narrow a search to one register, use include_dataset.
| Collection | Match path | Contents |
|---|---|---|
| default | /api/match/default | Every active record: sanctions, PEPs, debarment, enforcement and other risk lists. |
| sanctions | /api/match/sanctions | Sanctions listings only. |
| peps | /api/match/peps | Politically exposed persons. |
| crime | /api/match/crime | Debarment, tax warrants and insolvency notices. |
| maritime | /api/match/maritime | Vessels, and records that carry an IMO number. |
Map customer fields to the match request
A match request sends up to 20 named queries in one body, each with a schema and properties. Use your own customer or counterparty ID as the query key, so every response maps back to your record.
| Your field | Match field | Rules |
|---|---|---|
| Name, or each spelling you hold | properties.name | Array of up to 5 strings. Add a transliterated form when you hold one. Required unless imoNumber or mmsi is sent. |
| Date of birth | properties.birthDate | YYYY, YYYY-MM or YYYY-MM-DD. Send the precision you hold; a year alone is valid. |
| Nationality (person) | properties.nationality | Two-letter ISO 3166 code, such as ca or ru. |
| Country of incorporation (company) | properties.country | Two-letter code. The same field also takes a vessel's flag. |
| IMO number (vessel) | properties.imoNumber | Seven digits, such as 9418286. |
| MMSI (vessel) | properties.mmsi | Nine digits. |
| Type of party | schema | Person, Company, Organization, LegalEntity or Vessel. A record of a different type lowers the score (type_mismatch). Send the type you know rather than LegalEntity by default. |
A starting point that maps a customer record, sends a batch with retries, and prints the top result for each party.
import os, time, json, requests
BASE = os.environ.get("CROWN_API_BASE", "https://api.crownintelligence.net")
session = requests.Session()
session.headers["User-Agent"] = "my-screening-tool/1.0 ([email protected])"
def to_query(customer):
"""Map one customer record to a match query (see the field table above)."""
props = {"name": customer["names"][:5]}
if customer.get("birth_date"):
props["birthDate"] = [customer["birth_date"]] # YYYY, YYYY-MM or YYYY-MM-DD
if customer.get("nationality"):
props["nationality"] = [customer["nationality"]] # ISO 3166-1 alpha-2, e.g. "ca"
if customer.get("imo"):
props["imoNumber"] = [customer["imo"]] # seven digits
return {"schema": customer["schema"], "properties": props}
def match(collection, queries, limit=5, threshold=0.75, changed_since=None, retries=3):
"""POST /api/match with retries for 429 (honour Retry-After) and 5xx (back off)."""
params = {"limit": limit, "threshold": threshold}
if changed_since:
params["changed_since"] = changed_since
for attempt in range(retries + 1):
r = session.post(f"{BASE}/api/match/{collection}", params=params,
json={"queries": queries}, timeout=60)
if r.status_code == 429 and attempt < retries:
time.sleep(int(r.headers.get("Retry-After", "5")))
continue
if r.status_code >= 500 and attempt < retries:
time.sleep(2 ** attempt)
continue
r.raise_for_status()
return r.json()
customers = {
"C-1001": {"schema": "Person", "names": ["Igor Putin", "Игорь Путин"], "birth_date": "1953-03-30", "nationality": "ru"},
"C-1002": {"schema": "Vessel", "names": ["Selam"], "imo": "9418286"},
"C-1003": {"schema": "Person", "names": ["Zz Crown Checklist Testperson"], "birth_date": "1980", "nationality": "ca"},
}
queries = {cid: to_query(c) for cid, c in customers.items()}
out = match("default", queries, limit=3, threshold=0.75)
for cid, answer in out["responses"].items():
print(cid, answer["status"], "candidates:", answer["total"])
for hit in answer["results"][:1]:
print(" top:", hit["caption"], hit["score"], "match" if hit["match"] else "no match", hit["datasets"][:1])
print("checked_at:", out["checked_at"], "| collection:", out["collection"], "| threshold:", out["threshold"])
Set the threshold and read the match flag
Each result has a score from 0 to 1 and a match flag, which is true when the score is at or above threshold. The default threshold is 0.75 and the default limit is 5 results per query (up to 50). Results below the threshold still come back with match set to false, so reviewers can see near misses.
| Feature | What it measures | Effect on the score |
|---|---|---|
name_match | Word-by-word alignment of the query name with the record's names and aliases. | Sets the base score. |
dob_match | Birth date, at the record's precision. | +0.10 on a match; 0 when only the year matches; −0.25 when different. |
country_match | Nationality or country code. | +0.05 on a match; −0.10 when different. |
identifier_match | IMO number. | At least 0.98 on a match; −0.30 when different. |
type_mismatch | The record is a different type from schema. | −0.20. |
Handle possible matches
A result with match set to true is a possible match, not a finding. Route it to a review queue, and keep a decision for each one.
Log what was checked
We do not store query bodies, so your system must keep its own record of every check, including checks with no match. Store the response as received, and add the fields below.
| Field to log | Where it comes from |
|---|---|
| Your customer or counterparty ID | Your system. |
| Query as sent | Your request body. |
| Collection and its version | The collection name you used, and its version from /api/catalog at the time of the check. |
| Algorithm, threshold and limit | The query parameters you sent. The response echoes algorithm, threshold and limit. |
| Time of the check | checked_at in the response (UTC). |
| Record ID, merged entity, score and features | Each result's id, merged_entity, score and features. |
| Registers, topics and programme | Each result's datasets, topics and programme. |
| Decision, reviewer and decision date | Your review queue. |
Re-screen and monitor
Lists change. A party cleared last month can match after a new listing. Re-screen on a schedule, and when a collection is rebuilt.
import os, json, requests
BASE = os.environ.get("CROWN_API_BASE", "https://api.crownintelligence.net")
# Versions now, to compare with the versions stored at the last run
catalog = requests.get(f"{BASE}/api/catalog", timeout=30).json()
versions = {c["name"]: c["version"] for c in catalog["collections"]}
last_run = {"sanctions": "20261004022212-jhp"} # the version stored at the last run
changed = [name for name, v in versions.items() if last_run.get(name) not in (None, v)]
print("collections rebuilt since the last run:", changed)
# Only records added or changed since the last run (ISO 8601 date or time)
body = {"queries": {"C-1001": {"schema": "Person", "properties": {"name": ["Igor Putin"], "birthDate": ["1953"]}}}}
r = requests.post(f"{BASE}/api/match/sanctions", params={"limit": 10, "changed_since": "2026-10-01"},
json=body, timeout=60)
r.raise_for_status()
answer = r.json()["responses"]["C-1001"]
print("changed since 2026-10-01:", answer["total"], "candidates;", r.json()["filters"]["changed_since"])
# Entity lookup for the review screen
ent = requests.get(f"{BASE}/api/entity/379781867", timeout=30).json()
print(ent["id"], ent["name"], ent["register"], ent["list_type"], ent["listings"][0]["register"])
Rate limits, retries and volume
The data service has a per-address request limit that the operator can switch on. It is off today, and no figure is published. The terms reserve the right to apply limits or volume caps to automated use, so agree your volumes with us before you send a large batch.
Test with known names
Test the integration against names with known listings before go-live, and keep the test set in version control. Include:
One check we ran for this page: the query Igor Putin, birth year 1953 and nationality ru, against the sanctions collection with a threshold of 0.8 returned the sanctions listing at score 1 with match set to true. Results depend on the build in use, so record the collection version with each test run and repeat the tests after each rebuild.
Go-live checks
Samples
The samples below are starting points, so run them in a test environment before you rely on them. Set CROWN_API_BASE to change the base address; the default is the public service.
Node.js: match request
const BASE = process.env.CROWN_API_BASE ?? "https://api.crownintelligence.net";
const queries = {
vessel: { schema: "Vessel", properties: { name: ["Selam"], imoNumber: ["9418286"] } },
person: { schema: "Person", properties: { name: ["Igor Putin"], birthDate: ["1953-03-30"], nationality: ["ru"] } },
};
const params = new URLSearchParams({ limit: "3", threshold: "0.75" });
const res = await fetch(`${BASE}/api/match/maritime?${params}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ queries }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
for (const [key, answer] of Object.entries(data.responses)) {
const top = answer.results[0];
console.log(key, answer.status, answer.total, top ? [top.caption, top.score, top.match] : "no hits");
}
curl: collection versions and one match
# Versions of each collection, to see when a list was rebuilt
curl -s "$CROWN_API_BASE/api/catalog" | python3 -c 'import json,sys; [print(c["name"], c["version"]) for c in json.load(sys.stdin)["collections"]]'
# One party against the sanctions collection
curl -s -X POST "$CROWN_API_BASE/api/match/sanctions?limit=3&threshold=0.8&topics=sanction" \
-H "Content-Type: application/json" \
-d '{"queries":{"q1":{"schema":"Person","properties":{"name":["Igor Putin"],"birthDate":["1953"],"nationality":["ru"]}}}}'
Last updated 2026-10-10