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.
Keep your key in an environment variable. Our examples read it from OPENSANCTIONS_API_KEY.
Sign up to create one or learn more about the API. If you sign up using a business email address, a free trial key will be generated for you.
We issue free API keys to journalists, anti-corruption activists and academic research projects related to sanctions policy.
# /// 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.
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.
See an example of an entity on the OpenSanctions website. It includes all the properties from all the source datasets, as well as all related entities under Relationships.
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.