Make and interpret your first screening call using the /match endpoint.
Make and interpret your first screening call using the /match endpoint.
The /match endpoint uses query-by-example: you describe an entity in as much detail as you can, and the API returns ranked candidates from the database. You can experiment with the /match endpoint via the advanced screening search.
Requests are authenticated with an API key. Sign up to create one — if you sign up with a business email address, we generate a free trial key for you.
We also issue free API keys for public-interest work: journalism, civil-society advocacy, and academic research.
Keep your key in an environment variable; the examples below read it from OPENSANCTIONS_API_KEY. See Authentication for details.
# /// script
# dependencies = ["requests"]
# ///
# Run with: uv run quickstart.py
import os
import requests
BASE_URL = "https://api.opensanctions.org"
API_ENDPOINT = "match"
DATASET = "default"
API_KEY = os.environ.get("OPENSANCTIONS_API_KEY")
session = requests.Session()
session.headers["Authorization"] = f"ApiKey {API_KEY}"
query = {
"schema": "Person",
"properties": {
"firstName": ["Aleksandr"],
"lastName": ["Zacharov"],
"birthDate": ["1965"],
},
}
response = session.post(
f"{BASE_URL}/{API_ENDPOINT}/{DATASET}",
json={"queries": {"q": query}},
)
response.raise_for_status()
results = response.json()["responses"]["q"]["results"]
for r in results:
print(f"\n{r['caption']}, ID: {r['id']}, Match score: {r['score']}")
print('Available information:\n ', ', '.join(list(r.keys())))
if 'properties' in r.keys():
print('Available properties:\n ', ', '.join(list(r['properties'].keys())))
if 'topics' in r['properties']:
print('Risk topics:\n ', ', '.join(list(r['properties']['topics'])))
This is a basic request for a person. Run it and you should see output for at least one match.
The examples in these docs use plain HTTP requests so you can see exactly how the API works — and translate the calls into any programming language. For Python integrations, our yente-client package (pip install yente-client) wraps the same endpoints in a typed SDK with entity classes and structured error handling. It also ships yente-cli, a command-line tool for running match and search queries from the shell, and an MCP server that exposes screening to LLM agents.
There's much more you can do — learn about scoping a query, how filtering works, which properties to supply for which schema types, and tuning the matching algorithm, in the deep dive on the /match request:
This response has been condensed for readability:
{
"responses": {
"q": {
"status": 200,
"results": [
{
"id": "NK-aU5ybkbRFJucf8YMwsJvDw",
"caption": "Alexander Vyacheslavovich ZAKHAROV",
"schema": "Person",
"properties": {...}, # expanded below
"datasets": ["ua_nsdc_sanctions", "fr_tresor_gels_avoir", "us_trade_csl", "us_ofac_sdn", "eu_fsf", ...],
"referents": ["usgsa-s4mrvqj3g", "fr-ga-7952", "gb-fcdo-rus2051", "eu-fsf-eu-12789-10", ...],
"target": true,
"first_seen": "2023-07-19T18:02:43",
"last_seen": "2026-03-25T12:53:09",
"last_change": "2026-02-16T16:44:18",
"score": 0.9199999999999999,
"explanations": {
"name_match": {
"detail": "['aleksandr'≈'aleksandr' symbolMatch [NAME:Q17501806]: 1.00, weight 1.00] ['zacharov'≈'zacharov' symbolMatch [NAME:Q4188781]: 1.00, weight 1.30] ['vjaceslavovic' extraResultPart: 0.00, weight 0.20]",
"score": 0.9199999999999999,
"query": "Aleksandr Zacharov",
"candidate": "Zacharov Aleksandr Vjačeslavovič"
},
"vessel_imo_mmsi_match": {
"detail": "Not a vessel",
"score": 0.0,
"query": None,
"candidate": None
}
},
"match": true
},
{...}
],
"total": { "value": 2, "relation": "eq" },
"query": {...}
}
},
"limit": 5
}
The top-level response for each query (q in our example) includes a results list. The limit, set to 5 by default, is the maximum number of results returned per query. Each result describes a potentially matching entity. Some key fields on these:
id — the entity identifier (e.g. NK-…). Use this to fetch full details via /entities/<id>.properties — a full description of the entity, aggregated from all source datasets.datasets — the data sources this entity appears in.score — the confidence that this is a true match according to the algorithm used. The explanations explain how the algorithm arrived at the match score.The (trimmed) properties for the first result look like:
"properties": {
"lastName": ["ZAKHAROV", "Zakharov", "Zacharov", "Захаров"],
"position": ["Owner of LLC CST", "Propriétaire de LLC CST"],
"middleName": ["Vyacheslavovich", "Вячеславович", "Vjačeslavovič"],
"country": ["ru"],
"birthDate": ["1965-09-21"],
"firstName": ["Александр", "Aleksandr Vyacheslavovich", "ALEXANDER VYACHESLAVOVICH", "Aleksandr", "Alexander"],
"topics": ["corp.disqual", "sanction", "debarment"],
"programId": ["EU-UKR", "SECO-UKRAINE", "GB-RUS", "UA-SA1644", "US-RUSHAR", "NZ-RSA2022"]
}
topics is a key property listing the risk tags on the entity, such as sanction, role.pep, or debarment. This is the fastest way to understand what kind of risk a match carries.
Properties are always multi-valued, since they're aggregated from all source datasets. From the FollowTheMoney (FtM) docs:
First, multi-valued properties are a common thing in the FtM domain: companies have multiple official forms of their name, people can have multiple citizenships, names, and, more often than you'd think, ambiguous birth dates.
So, if a Person entity has two birthdays, it simply means that different datasets have differing data on the real-life person — not that you should plan to attend two parties.
/entities endpoint If you want to retrieve additional details for a matched entity, use the /entities/<id> endpoint, where <id> is the id attribute on one of the results.
A /match result and an /entities response contain the same core fields, but /entities includes related entities inline by default, like Sanction, Ownership, Directorship, and Family.
The below follows from your first request above:
...
nested_entity_response = session.get(
f"{BASE_URL}/entities/{results[0]['id']}"
)
nested_entity_json = nested_entity_response.json()
sanctions = nested_entity_json["properties"]["sanctions"]
for sanction in sanctions:
print(f"{nested_entity_json['caption']} is sanctioned by {sanction['properties']['authority']}")
Paste it into the same terminal as before to see the issuing authority of each sanction related to the first match.