# Sanctions Screening – OFAC, EU, UK, UN Lists (`sfoln/sanctions-screening`) Actor

Screen people, companies, vessels and aircraft against the official OFAC SDN and Consolidated, UN Security Council, EU and UK sanctions lists. Fuzzy, alias-aware matching with transliteration, date-of-birth and country checks, and list versions stamped on every result. $3 per 1,000 names.

- **URL**: https://apify.com/sfoln/sanctions-screening.md
- **Developed by:** [SFOLN LLC](https://apify.com/sfoln) (community)
- **Categories:** Automation, Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 screened names

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 – OFAC, EU, UK, UN Lists** checks names of people, companies, vessels and aircraft against the official sanctions lists (OFAC SDN, OFAC Consolidated non-SDN, UN Security Council Consolidated List, EU Consolidated Financial Sanctions List and UK Sanctions List) and returns one record per name: `potential_match` or `no_match`, the best score and the matching list entries with their programs, aliases, dates of birth and nationalities. Built for onboarding, payment and vendor checks, compliance tools and AI agents. $0.003 per screened name; lists are downloaded straight from the publishers and every record carries the list versions it was screened against.

- **What you get:** `matchStatus`, `bestScore`, and per match the list, entry id, listed name, matched name or alias, programs, date of birth, nationalities, countries, listing date, score and the reasons behind it; plus `listsVersion` (source URL, fetch time, publication date and record count per list).
- **Matching:** transliteration of any script (Cyrillic, Arabic, Greek, CJK …), legal forms ignored (LLC, GmbH, OOO, PJSC …), word order and missing middle names tolerated, spelling variants scored with Jaro-Winkler and an IDF-weighted token set, aliases and weak aliases included; optional date of birth, country and type checks.
- **Price:** $0.003 per screened name (`name-screened`), whatever the outcome; invalid names and errors are not charged.
- **Works everywhere:** API, MCP for AI agents, Make, Zapier, n8n, schedules and webhooks.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `names` | string\[] | – | Names to screen, one per line. Names contain commas ("PUTIN, Vladimir"), so a plain string is split on line breaks and semicolons only. |
| `entities` | object\[] | – | Names with details: `{ "name", "type", "dateOfBirth", "country" }`. `type` is person, entity, vessel or aircraft; `dateOfBirth` YYYY-MM-DD, YYYY-MM or YYYY; `country` an ISO code or a country name. |
| `entityType` | string | any | Type for all `names`: any, person, entity (companies, organisations, vessels, aircraft), vessel or aircraft. |
| `lists` | string\[] | all five | `ofac-sdn`, `ofac-cons`, `un`, `eu`, `uk`. |
| `threshold` | number | 0.85 | Minimum score (0.5–1) of a reported match. |
| `maxMatchesPerName` | integer | 5 | Matches returned per name (1–25), best first. |
| `includeWeakAliases` | boolean | true | Also compare with low-quality aliases (their scores are 5% lower). |
| `cacheMaxAgeHours` | integer | 24 | Reuse lists your account downloaded within this many hours (0 = always download). |
| `maxItems` | integer | 10000 | Safety cap on names per run; further names are ignored and counted as `inputItemsDropped` in the `OUTPUT` record. |
| `includeRaw` | boolean | false | Attach the complete list entries of the matches under `raw`. |

```json
{
  "names": ["AEROCARIBBEAN AIRLINES", "Acme Bakery Ltd"],
  "entities": [{ "name": "Ivan Petrov", "type": "person", "dateOfBirth": "1960-03-12", "country": "RU" }],
  "threshold": 0.85
}
```

### Output

One dataset record per name (matches trimmed to one for brevity):

```json
{
  "input": "AEROCARIBBEAN AIRLINES",
  "status": "ok",
  "charged": true,
  "chargedEvent": "name-screened",
  "source": "sanctions-lists",
  "fetchedAt": "2026-10-14T22:40:00.000Z",
  "name": "AEROCARIBBEAN AIRLINES",
  "entityType": "any",
  "dateOfBirth": null,
  "country": null,
  "normalizedName": "AEROCARIBBEAN AIRLINES",
  "matchStatus": "potential_match",
  "bestScore": 1,
  "threshold": 0.85,
  "matchCount": 1,
  "totalMatches": 1,
  "topMatchName": "AEROCARIBBEAN AIRLINES",
  "topMatchList": "ofac-sdn",
  "topMatchScore": 1,
  "matches": [
    {
      "list": "ofac-sdn",
      "listName": "OFAC Specially Designated Nationals (SDN) List",
      "entryId": "36",
      "name": "AEROCARIBBEAN AIRLINES",
      "matchedName": "AEROCARIBBEAN AIRLINES",
      "matchedNameType": "primary",
      "aliases": ["AERO-CARIBBEAN"],
      "type": "entity",
      "programs": ["CUBA"],
      "dateOfBirth": [],
      "nationalities": [],
      "countries": ["CU"],
      "listedOn": null,
      "ids": [],
      "remarks": null,
      "score": 1,
      "nameScore": 1,
      "reasons": ["name_exact"],
      "sourceUrl": "https://sanctionssearch.ofac.treas.gov/"
    }
  ],
  "listsComplete": true,
  "listsStale": false,
  "listsScreened": ["ofac-sdn", "ofac-cons", "un", "eu", "uk"],
  "listsVersion": {
    "ofac-sdn": {
      "list": "ofac-sdn",
      "name": "OFAC Specially Designated Nationals (SDN) List",
      "status": "ok",
      "sourceUrls": ["https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/SDN.CSV"],
      "fetchedAt": "2026-10-14T22:39:12.000Z",
      "publishedAt": "2026-10-14T14:02:30.000Z",
      "recordCount": 19430,
      "fromCache": false
    }
  },
  "screenedAt": "2026-10-14T22:40:00.000Z"
}
```

| Field | Description |
| --- | --- |
| `matchStatus` | `potential_match` when at least one list entry scores at or above `threshold`, else `no_match`. |
| `bestScore` | Highest score among the entries compared (0–1), also when below the threshold. Entries that cannot reach the threshold or 0.7, whichever is lower, are not compared, so `0` means no entry came that close. |
| `matchCount` / `totalMatches` | Matches returned (at most `maxMatchesPerName`) / all entries at or above the threshold. |
| `matches[]` | Potential matches, best first: `list`, `entryId`, listed `name`, `matchedName` and `matchedNameType` (primary, alias, weak_alias, former, non_latin), `aliases`, `type`, `programs`, `dateOfBirth`, `nationalities`, `countries`, `listedOn`, `ids` (UN reference, IMO …), `remarks`, `score`, `nameScore` (before date/country adjustments) and `reasons` (name_exact, name_similar, alias, weak_alias, transliterated, dob_exact, dob_year, dob_mismatch, country_match). |
| `topMatchName` / `topMatchList` / `topMatchScore` | The best match, as flat columns for spreadsheets. |
| `listsComplete` | `true` when every selected list was screened. `false` only on a potential match while a selected list could not be loaded (a name without a match is then an uncharged `lists_incomplete` error, see below). |
| `listsStale` | `true` when a selected list was screened against an older cached copy (up to 7 days old) because its download failed in this run; `listsVersion` shows when that copy was fetched. |
| `listsVersion` | Per list: `status` (ok, stale, unavailable), `sourceUrls`, `fetchedAt`, `publishedAt`, `recordCount`, `fromCache`. Also saved as the `LISTS_VERSION` record of the run's key-value store. |

`status` is one of:

- `ok`: the name was screened (charged, whether or not it matched);
- `error`: the name could not be screened (not charged); `error.code` `lists_incomplete` when it has no match on the lists that loaded but a selected list could not be loaded, so it is not cleared against every selected list;
- `skipped_invalid`: the name has no letters or digits (not charged);
- `skipped_budget`: your **Maximum cost per run** was reached (not charged);
- `skipped_free_plan`: beyond the free-plan limit below (not charged);
- `skipped_unmetered`: the run is not pay-per-event (not charged).

If none of the selected lists, or the OFAC SDN list, can be downloaded, the run stops before screening anything (nothing is charged). When a download fails, a cached copy from the last 7 days is used instead (`listsStale: true` and a warning on each record). A list with neither a download nor such a copy is unavailable: names that match on the other lists are still delivered and charged (`listsComplete: false`), while names without a match come back as uncharged `lists_incomplete` errors, because they are not cleared against every selected list. Run those again later, or leave the unavailable list out of `lists`.

### Pricing

Pay-per-event. You are charged only for screened names:

| Event | Price | When |
| --- | --- | --- |
| `name-screened` | $0.003 | once per name screened against the lists (`status: "ok"`), potential match or not; a name without a match while a selected list is unavailable (`lists_incomplete`) is not charged |

Screening 1,000 names costs $3. Set **Maximum cost per run** on the run to cap spend; the Actor stops cleanly (records marked `skipped_budget`) when the cap is reached. Accounts on the Apify free plan can process up to 10 names per run; further names are returned with `status: "skipped_free_plan"` and are not charged. Upgrade to any paid Apify plan for full batches.

### Use with AI agents (MCP)

Use this Actor from Claude, ChatGPT, Cursor, VS Code or any other MCP client through the [Apify MCP server](https://mcp.apify.com). Add this server URL to your client and sign in with your Apify account when asked:

```text
https://mcp.apify.com?tools=sfoln/sanctions-screening
```

To run the MCP server locally instead, start `npx -y @apify/actors-mcp-server --tools sfoln/sanctions-screening` with your `APIFY_TOKEN` in the environment.

Example prompt:

> Screen "AEROCARIBBEAN AIRLINES" and "Acme Bakery Ltd" against the OFAC, UN, EU and UK sanctions lists and tell me which ones are potential matches.

Runs started by an agent are billed like any other run (see Pricing). Agents can cap what a run may spend with `maxTotalChargeUsd`.

### API usage

Run the Actor and get its dataset items in one call (the call waits up to 5 minutes for the run to finish):

```bash
curl -X POST "https://api.apify.com/v2/acts/sfoln~sanctions-screening/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"names": ["AEROCARIBBEAN AIRLINES", "Acme Bakery Ltd"]}'
```

For large inputs, start the run asynchronously and download the dataset when it has finished:

```bash
## 1. Start a run: the response holds the run ID (data.id) and its dataset ID (data.defaultDatasetId)
curl -X POST "https://api.apify.com/v2/acts/sfoln~sanctions-screening/runs" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"names": ["AEROCARIBBEAN AIRLINES", "Acme Bakery Ltd"]}'
## 2. Repeat until data.status is SUCCEEDED (each call waits up to 60 s)
curl "https://api.apify.com/v2/actor-runs/<runId>?waitForFinish=60" -H "Authorization: Bearer <APIFY_TOKEN>"
## 3. Download the results (format=json, csv or xlsx)
curl "https://api.apify.com/v2/datasets/<defaultDatasetId>/items?format=json" -H "Authorization: Bearer <APIFY_TOKEN>"
```

Python ([apify-client](https://pypi.org/project/apify-client/)):

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("sfoln/sanctions-screening").call(run_input={"names": ["AEROCARIBBEAN AIRLINES", "Acme Bakery Ltd"]})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

Instead of polling, you can attach a webhook to the run (see Integrations). The API tab of this page shows the same calls for JavaScript and the Apify CLI.

### Integrations

- **Make, Zapier and n8n:** run `sfoln/sanctions-screening` with the Apify app (in n8n, the Apify node) and pass the dataset items to the next step of your workflow.
- **Google Sheets:** add the items to a sheet with the Google Sheets step of Make, Zapier or n8n, or download them as CSV or Excel from the run's Storage tab or the API (`format=csv` or `format=xlsx`).
- **Webhooks:** get a POST request when a run succeeds or fails: add a webhook in the Actor's Integrations tab, or pass `webhooks` when you start a run through the API.
- **Schedules:** run it hourly, daily or weekly with the same input from Apify Schedules; every run keeps its results in its own dataset.
- **More:** Slack, Google Drive, GitHub, LangChain, LlamaIndex and other [Apify integrations](https://docs.apify.com/platform/integrations).

### Related Actors

- [KYB Company Check – Registry, LEI, VAT](https://apify.com/sfoln/kyb-company-check)
- [LEI Lookup – GLEIF Legal Entity Search](https://apify.com/sfoln/lei-lookup)
- [EU VAT Number Validator (VIES)](https://apify.com/sfoln/vat-number-validator)
- [Domain WHOIS & DNS Lookup](https://apify.com/sfoln/domain-intelligence)
- [Website Contact Details Scraper](https://apify.com/sfoln/company-contacts-finder)

See [all our Actors](https://apify.com/sfoln) on Apify Store.

### FAQ

#### How much does it cost, and am I charged for failed items?

You pay per event at the prices under Pricing above, plus a run start fee of $0.00005 per GB of memory, with no subscription; platform usage is included. Errors and skipped items (`skipped_…` statuses) are never charged; a lookup that finds nothing is charged only where Pricing says so, and every record's `charged` field shows whether it was billed. Set **Maximum cost per run** to cap what a single run can spend.

#### How fresh is the data?

Each run queries its source while it runs, so you get what the source returns at that moment. Source-specific details, such as how often a provider refreshes its own database, are under Limits & notes.

#### What are the limits?

Per-run caps (for example the maximum number of items) are listed under Input, source limits under Limits & notes, and the free-plan limit under Pricing. For big batches, start the run asynchronously (see API usage) and read the dataset when it has finished.

#### Can I schedule runs or get results by webhook?

Yes. Create a schedule in Apify Console to run it hourly, daily or weekly with the same input, and add a webhook to get a POST request when a run finishes (see Integrations).

#### What does this Actor record about my runs?

This Actor records operational metrics about each run (item counts, duration, charges, error codes and a pseudonymous hash of your Apify user ID) to monitor reliability and costs. It never stores your input or results.

#### Which sanctions lists are included?

OFAC Specially Designated Nationals (SDN), the OFAC Consolidated (non-SDN) lists, the UN Security Council Consolidated List, the EU Consolidated Financial Sanctions List and the UK Sanctions List, downloaded from the official publishers. Every record shows each list's source URL, download time, publication date and record count (listsVersion).

#### How does the matching work?

Names are transliterated to ASCII (Cyrillic, Arabic, Greek and other scripts), normalised (case, punctuation, legal forms such as LLC or OOO) and compared with every primary name and alias using a weighted token-set and Jaro-Winkler score that tolerates word order, missing middle names and spelling variants. A date of birth or country raises the score when the list entry agrees; a distant date of birth lowers it.

#### Is a potential match a confirmed sanctions hit?

No. A potential match means a list entry's name is similar enough to need a review; compare the date of birth, nationality, programs and remarks in the match before deciding.

#### How fresh are the lists?

Lists are downloaded at most once every 24 hours per Apify account (set List cache age to 0 to always download). OFAC and the UN publish daily; the EU and UK update on each new designation.

#### How do I get help?

Open an issue in the Actor's Issues tab and include the link to the run, so we can reproduce the problem. Feature requests are welcome there too.

### Limits & notes

- A potential match is a lead for review, not a determination: compare dates of birth, nationalities, programs and remarks before acting.
- Lists: OFAC SDN and Consolidated (US Treasury CSV exports), UN Security Council Consolidated List (XML), EU Consolidated Financial Sanctions List (FSF XML 1.1) and UK Sanctions List (FCDO XML). Other national lists (Canada, Australia, Switzerland) and PEP or adverse-media data are not included.
- The first run of the day downloads about 60 MB of list files (the EU file alone takes up to 20 s) and needs 1 GB of memory; later runs within `cacheMaxAgeHours` read the parsed lists from your account's `sanctions-screening-cache` key-value store and start in seconds.
- OFAC's CSV exports do not mark weak aliases; weak a.k.a.s that OFAC lists in remarks are included as `weak_alias`.
- Up to 100,000 names per run (`maxItems`); screening takes a few milliseconds per name.

# Actor input Schema

## `names` (type: `array`):

Names to screen, one per line ("AEROCARIBBEAN AIRLINES", "PUTIN, Vladimir", "Acme Trading GmbH"). Any script works: non-Latin names are transliterated. Accounts on the Apify free plan can process up to 10 names per run.

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

Optional: names with secondary identifiers, e.g. \[{"name": "Ivan Petrov", "type": "person", "dateOfBirth": "1960-03-12", "country": "RU"}]. type is person, entity, vessel or aircraft; dateOfBirth is YYYY-MM-DD, YYYY-MM or YYYY; country is an ISO code or a name. Matching dates of birth and countries raise the score, distant dates of birth lower it.

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

Which list entries the names are compared with: any (default), person (individuals only), entity (companies and organisations, incl. vessels and aircraft), vessel or aircraft. An entity type in "Names with details" overrides it.

## `lists` (type: `array`):

Lists to screen against (default: all five). ofac-sdn = OFAC Specially Designated Nationals, ofac-cons = OFAC Consolidated non-SDN lists, un = UN Security Council Consolidated List, eu = EU Consolidated Financial Sanctions List, uk = UK Sanctions List.

## `threshold` (type: `number`):

Minimum score (0.5–1) for a list entry to be reported as a potential match. Lower finds more spelling variants and more false positives; 1 reports exact name matches only.

## `maxMatchesPerName` (type: `integer`):

How many matching list entries are returned per name, best first.

## `includeWeakAliases` (type: `boolean`):

Also compare with low-quality aliases ("weak a.k.a." on the lists). Their matches score 5% lower.

## `cacheMaxAgeHours` (type: `integer`):

Reuse lists this run's account downloaded within this many hours (kept in the key-value store "sanctions-screening-cache"); 0 downloads fresh copies. Every record shows the list versions it was screened against.

## `maxItems` (type: `integer`):

Safety cap on how many names are screened in this run; further names are ignored and counted in the OUTPUT record.

## `includeRaw` (type: `boolean`):

Attach the complete list entries of the reported matches (all names, remarks) under raw.

## Actor input object example

```json
{
  "names": [
    "AEROCARIBBEAN AIRLINES",
    "Acme Bakery Ltd"
  ],
  "entityType": "any",
  "lists": [
    "ofac-sdn",
    "ofac-cons",
    "un",
    "eu",
    "uk"
  ],
  "threshold": 0.85,
  "maxMatchesPerName": 5,
  "includeWeakAliases": true,
  "cacheMaxAgeHours": 24,
  "maxItems": 10000,
  "includeRaw": false
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "names": [
        "AEROCARIBBEAN AIRLINES",
        "Acme Bakery Ltd"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sfoln/sanctions-screening").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 = { "names": [
        "AEROCARIBBEAN AIRLINES",
        "Acme Bakery Ltd",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("sfoln/sanctions-screening").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 '{
  "names": [
    "AEROCARIBBEAN AIRLINES",
    "Acme Bakery Ltd"
  ]
}' |
apify call sfoln/sanctions-screening --silent --output-dataset

```

## MCP server setup

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

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/rUEWI63JqkGhGGlvf/builds/2LmYxwcWBhJjyOxr6/openapi.json
