# Sanctions Screening Check: OFAC, EU and UK Lists (`accountable_eel/sanctions-screening-check`) Actor

Screen people and company names against three official sanctions lists: US Treasury OFAC (SDN), the EU consolidated list and the UK Sanctions List. Screen a supplier list, or check a person with a date of birth. Aliases and scored reasons, to Google Sheets. A screening aid, not a compliance verdict.

- **URL**: https://apify.com/accountable_eel/sanctions-screening-check.md
- **Developed by:** [Adriana Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 entity screeneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Sanctions Screening Check: OFAC, EU and UK Lists

**Screen a list of people or company names against three official sanctions lists at once, and get
back the reason behind every score.** Each run downloads the US Treasury OFAC lists (SDN plus the
consolidated non-SDN list), the EU consolidated financial sanctions list, and the UK Sanctions List
straight from the issuing authority, compares every name you give it against each list's primary
names *and* its aliases, and returns one row per name with the candidate matches, their scores, and
a plain-English breakdown of how each score was reached.

**This is a screening aid that surfaces candidate matches for human review, not a compliance
determination.** It tells you which published list entries resemble the names you hold. Deciding
whether a candidate match is in fact the same person or company, and what to do about it, is work
for a human with the underlying evidence in front of them.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `entities` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~sanctions-screening-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"entities":["Rosneft Oil Company","Gazprombank","Acme Office Supplies Ltd"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

### Input

```json
{
  "entities": [
    "Rosneft Oil Company",
    "Gazprombank",
    "Acme Office Supplies Ltd"
  ]
}
```

One name per line — a person or an organisation, exactly as you hold it (registered company name, full legal name). Up to 1,000 per run. Every name is compared against the primary names AND the aliases on each list you select. Accepted formats: Rosneft Oil Company, Aiman Muhammed Rabi Al-Zawahiri, Gazprombank Joint Stock Company.

Everything else is optional:

| Field | Default | What it does |
| --- | --- | --- |
| `entityType` | `any` | `individual`, `organisation`, or `any`. Narrows which list entries can match, and picks the normalisation: legal forms (Ltd, LLC, OOO, GmbH) come off organisation names, honorifics (Dr, Shaykh, General) off people's names. |
| `lists` | `all` | `all`, `ofac`, `eu` or `uk`. The price per name is the same either way. |
| `minScore` | `85` | Candidates below this are not reported. |
| `maxMatchesPerEntity` | `5` | How many candidates to show per name. The full count is always reported in `matchCount`. |
| `country` | — | A 2-letter ISO code for the entity you are screening. Agreement raises the score slightly; a contradiction lowers it by 10 and says so. |
| `dateOfBirth` | — | `YYYY` or `YYYY-MM-DD`, people only. Agreement raises the score slightly; a contradiction lowers it by 15 and says so. |

There is no API key anywhere in this actor, optional or otherwise. All three exports are free and
unauthenticated, and no proxy is used.

### Output

One row per name screened, for example:

| query | found | status | input | screeningStatus | matchCount | bestScore | matches | notes | disclaimer | screenedAt | listsChecked | entityCount | screenedCount | possibleMatchCount | results | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Rosneft Oil Company | true | OK | <name screened> | <result> | <candidate matches found> | <best score> | <candidate matches> | <notes> | <disclaimer> | <screened at> | <lists checked> | <names in this run> | <names fully screened> | <names with a possible match> | \<all results (full list)> | 1970-01-01T00:00:00.000Z |

`screeningStatus` is one of:

- **CLEAR** — no candidate scored at or above `minScore` on any list that was checked.
- **POSSIBLE_MATCH** — one or more candidates are worth a human look. `matches` holds them, each with
  `list`, `listEntryId`, `matchedName`, `matchedOn` (primary name or alias), `entryType`, `programs`,
  `nationalities`, `datesOfBirth`, `listedOn`, `score`, `scoreReasons` and `sourceUrl`.
- **PARTIAL** — at least one requested list could not be downloaded, so this name was **not** fully
  screened. A PARTIAL row is never charged.

`listsChecked` carries each list's own publication date, as stamped on the file by the authority
that published it, so a row is always auditable back to a specific edition of a specific list.

### Pricing

- **Entity screened**: $16 per 1,000 entitys

Plus a $0.01 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

You are charged **once per name fully screened**, whichever lists you select and however many
candidates come back. A CLEAR result is a delivered answer and is charged; a PARTIAL row is not.

### Example runs

Paste any of these into the Input tab and press Start. All three return rows with no key, no token
and no proxy.

**1. Screen a supplier list against all three lists.** The everyday run: paste your supplier or
counterparty names, get one row each.

```json
{
  "entities": ["Rosneft Oil Company", "Gazprombank", "Acme Office Supplies Ltd"],
  "entityType": "organisation",
  "lists": "all"
}
```

**2. OFAC SDN check only, for US-facing compliance.** Same screening, scoped to the US Treasury
lists, with more candidates shown per name.

```json
{
  "entities": ["Rosneft Oil Company", "Gazprombank", "Sovcomflot"],
  "entityType": "organisation",
  "lists": "ofac",
  "maxMatchesPerEntity": 10
}
```

**3. Screen a person, with a date of birth to separate namesakes.** The date is compared against
each list entry's own published date, and the comparison is spelled out in `scoreReasons`.

```json
{
  "entities": ["Aiman Muhammed Rabi Al-Zawahiri"],
  "entityType": "individual",
  "dateOfBirth": "1951-06-19"
}
```

### Where the data comes from

Every file is the issuing authority's own machine-readable export. Nothing here comes from an
aggregator, a mirror, or a third-party API.

| List | File | Format | Size | Entries / names |
| --- | --- | --- | --- | --- |
| **OFAC** (US Treasury) | `SDN.CSV`, `ALT.CSV`, `CONS_PRIM.CSV`, `CONS_ALT.CSV` from `sanctionslistservice.ofac.treas.gov` | headerless CSV | 5.7 / 1.1 / 0.26 / 0.07 MB | 19,452 SDN + 481 non-SDN rows, 21,330 alias rows |
| **EU** (European Commission, DG FISMA) | the consolidated financial sanctions list CSV linked from data.europa.eu | semicolon CSV, 118 columns | 25 MB | 43,891 rows, about 11,000 entities |
| **UK** (FCDO) | `UK-Sanctions-List.csv`, the CSV attachment of the gov.uk publication *The UK Sanctions List* | CSV with a leading report-date line | 50 MB | 58,499 name rows across 6,370 designations |

Measured 2026-10-01. Each list is downloaded once per run and cached for 24 hours in a named
key-value store keyed by list and date, so a second run the same day starts from the cache instead
of re-downloading 82 MB.

Dates of birth and nationalities are structured columns on the EU and UK lists. OFAC's CSV export
has no such columns — it publishes them as free text inside `Remarks` ("DOB 19 Jun 1951; POB Giza,
Egypt") — so they are parsed out of there rather than from the much larger SDN XML.

### How matching works

Deterministic and explained. No machine learning, no external API, no randomness: the same name
against the same edition of a list always produces the same score.

Both names are first normalised — case folded, accents removed (NFKD), punctuation dropped, legal
forms stripped from organisations and honorifics from people. Three independent comparisons then
run, and the score is the **highest** of them:

1. **Exact normalised** — 100 when the two normalised strings are identical.
2. **Word overlap**, in any order — a Dice overlap of the two token sets. Deliberately not
   "intersection over the smaller set", which would score a one-word query 100 against every long
   entry containing that word.
3. **Transliteration-tolerant** — the same shape, over tokens folded through a fixed
   transliteration map (KH/H, PH/F, W/V, Y/I, doubled letters, vowel-skeleton equality), so
   *Ayman Mohammad al-Zawahari* reaches *Aiman Muhammed Rabi Al-Zawahiri*.

Then the context fields adjust the number, and every adjustment is written into `scoreReasons`:

- a **date of birth** that matches adds 3 (1 if only the year matches); one that **contradicts** the
  entry subtracts 15.
- a **country** that matches adds 3; one that **contradicts** the entry subtracts 10.
- an entry that publishes **no** date of birth or nationality is never penalised for it, and the row
  says the field could not be checked either way.

A candidate whose **name alone** scored 95 or more is always reported, even when a conflict pushed
its final score below your `minScore`. Contradicting evidence lowers a score and is shown; it never
silently deletes a near-identical name.

**False-positive guard.** A candidate whose entire overlap with your input is made of generic
business words — *general*, *trading*, *international*, *group*, *holdings*, *company* — is
suppressed rather than shown at a low score, and the count of suppressions is reported in `notes`.
Screening "General Trading Company Ltd" returns CLEAR with an explanation, not the several hundred
sanctioned entities that also call themselves a general trading company.

### What this actor is not

- **Not a compliance determination.** It surfaces candidates. A human decides.
- **Not a PEP list.** Politically exposed persons are a separate, mostly commercial dataset. This
  actor screens sanctions lists only.
- **Not every sanctions list.** OFAC, EU and UK only. UN, Canadian, Australian, Swiss and Japanese
  lists are not covered, though the UN designations that the EU and UK have implemented do appear
  through those lists.
- **Not real time.** Each list changes on its own publication schedule, and a run reads whatever
  edition was published when it downloaded. `listsChecked[].publicationDate` always says which one.

### Limits and known gaps

- **Blocking.** The three lists hold roughly 120,000 names; scoring every one of them against every
  input with an edit-distance comparison would take minutes of CPU per run. A candidate is only
  scored when it shares at least one *folded* word with your input (the same transliteration map the
  scorer uses, so *Mohammad* and *Muhammed* block together). A listed name that shares no word at all
  with your input is therefore never scored, and so can never be reported.
- **Non-Latin scripts.** All three lists carry non-Latin spellings; names are compared after folding
  to ASCII, so a query written in Arabic or Cyrillic is normalised to nothing useful. Screen the
  Latin transliteration.
- **Vessels and aircraft** appear on the OFAC and UK lists and are matched under
  `entityType: organisation`, labelled `vessel` in `entryType`.
- **1,000 names per run.** Longer lists should be split across runs.
- **One row per list entry, not per alias.** An entity with nine aliases is reported once, under its
  best-scoring name, with a primary-name match winning a tie.

### Related actors

- **[Vendor Due Diligence: Company, VAT, LEI and Domain Checks](https://apify.com/accountable_eel/vendor-due-diligence-check)** —
  the other half of a KYB file: is this supplier a real, registered, reachable company? Screen the
  name here, verify the entity there.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~sanctions-screening-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"entities":["Rosneft Oil Company","Gazprombank","Acme Office Supplies Ltd"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~sanctions-screening-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"entities":["Rosneft Oil Company","Gazprombank","Acme Office Supplies Ltd"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~sanctions-screening-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"entities":["{{screening run}}"]}`, mapping the row's screening run into the `entities` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "OFAC EU UK sanctions screening check" — the agent will find and run this actor.

# Actor input Schema

## `entities` (type: `array`):

One name per line — a person or an organisation, exactly as you hold it (registered company name, full legal name). Up to 1,000 per run. Every name is compared against the primary names AND the aliases on each list you select. Accepted formats: Rosneft Oil Company, Aiman Muhammed Rabi Al-Zawahiri, Gazprombank Joint Stock Company. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `entityType` (type: `string`):

Narrows which list entries can match, and which normalisation runs: legal forms (Ltd, LLC, OOO, GmbH) are stripped from organisation names, honorifics (Dr, Shaykh, General) from people's names.

## `lists` (type: `string`):

Each list is downloaded from the issuing authority's own published export. You are charged once per name screened, whichever lists you pick.

## `minScore` (type: `integer`):

Candidates scoring below this are not reported. 85 is a deliberately cautious default. A candidate whose NAME alone scored 95 or more is always reported even if a date-of-birth or country conflict pushed its final score under your threshold.

## `maxMatchesPerEntity` (type: `integer`):

How many candidate matches to show per name, highest score first. The full count is always reported in "Candidate matches found". Up to 50.

## `country` (type: `string`):

Optional. A 2-letter country code (GB, RU, DE) for the entity you are screening. A list entry whose published nationality agrees scores slightly higher; one that contradicts it scores 10 lower, and says so. Entries that publish no nationality are never penalised.

## `dateOfBirth` (type: `string`):

Optional. YYYY or YYYY-MM-DD. A list entry whose published date of birth agrees scores slightly higher; one that contradicts it scores 15 lower, and says so. It never deletes a candidate that matched the name exactly.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each entity found gets its own row instead of being grouped under its screening run. You're still only charged once per screening run, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "entities": [
    "Rosneft Oil Company",
    "Gazprombank",
    "Acme Office Supplies Ltd"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "entityType": "any",
  "lists": "all",
  "minScore": 85,
  "maxMatchesPerEntity": 5,
  "country": "",
  "dateOfBirth": "",
  "columns": [
    "input",
    "screeningStatus",
    "matchCount",
    "bestScore",
    "matches",
    "notes",
    "disclaimer",
    "screenedAt",
    "listsChecked",
    "entityCount",
    "screenedCount",
    "possibleMatchCount",
    "results"
  ],
  "expandRows": true,
  "maxConcurrency": 1,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "entities": [
        "Rosneft Oil Company",
        "Gazprombank",
        "Acme Office Supplies Ltd"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/sanctions-screening-check").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "entities": [
        "Rosneft Oil Company",
        "Gazprombank",
        "Acme Office Supplies Ltd",
    ],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/sanctions-screening-check").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "entities": [
    "Rosneft Oil Company",
    "Gazprombank",
    "Acme Office Supplies Ltd"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/sanctions-screening-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/sanctions-screening-check"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/FOgGrhzzCcnW4Aufw/builds/G8chAyp12aL2UDACc/openapi.json
