# OFAC SDN and EU Sanctions Screening for Companies (`nightwave-owner/sanctions-entity-screening`) Actor

Screens company names against the OFAC SDN, OFAC Consolidated and EU sanctions lists with fuzzy matching. Returns each hit with score, programs, listing date, aliases, countries and IMO or registration numbers. Entities, vessels and aircraft only: individuals are never returned.

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

## Pricing

Pay per event

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 for Companies and Vessels (OFAC SDN, EU)

This actor checks company, vessel and aircraft names against the US Treasury's OFAC lists (the SDN list and the Consolidated non-SDN list) and the EU Consolidated Financial Sanctions List. Each name is compared with every listed name and alias using fuzzy matching. Every hit comes back as one row with a match score, the sanctions programs, the listing date, aliases, countries and identifiers such as IMO numbers, registration numbers and tax IDs.

Use it for KYB and AML checks of customers, suppliers and counterparties, to screen vessels before a charter, or to watch the lists for new designations on a daily schedule.

**Individuals are left out completely.** The actor only returns legal entities (companies, organizations), vessels and aircraft. Records of natural persons are dropped while the files are read, before any of their names are looked at, and never appear in any output field, aliases included. No personal data is processed or stored.

**This is list data, not legal or compliance advice.** The actor tells you which records on the official lists resemble a name. Whether a match is the same company, and what you may or may not do with it, is for you or your compliance adviser to decide.

### Example from a real run

Run `79LvS7eeC5zAbnesh` on Apify on 4 October 2026, with this input:

```json
{
  "names": ["Rosneft", "Bank Melli", "Sberbank"]
}
```

It returned 35 matches (5 for Rosneft, 9 for Bank Melli, 21 for Sberbank) in 38 seconds of run time, most of it spent downloading the lists. The first rows for each name, with some fields left out here to keep it short:

```json
[
  {
    "queryName": "Rosneft",
    "matchedName": "ROSNEFT",
    "matchScore": 100,
    "name": "OPEN JOINT-STOCK COMPANY ROSNEFT OIL COMPANY",
    "entityType": "entity",
    "list": "ofac",
    "listNames": ["Sectoral Sanctions Identifications List", "Consolidated List", "SDN List"],
    "referenceNumber": "17022",
    "programs": ["UKRAINE-EO13662", "RUSSIA-EO14024"],
    "listedOn": "2014-07-16",
    "lastChangedOn": "2025-10-22",
    "aliases": ["OJSC ROSNEFT OIL COMPANY", "ROSNEFT OIL COMPANY", "OAO ROSNEFT OIL COMPANY", "OIL COMPANY ROSNEFT", "ROSNEFT"],
    "countries": ["RU"],
    "identifiers": [
      { "type": "Registration number", "value": "1027700043502", "country": "RU" },
      { "type": "Government Gazette Number", "value": "00044428", "country": "RU" },
      { "type": "Tax ID", "value": "7706107510", "country": "RU" }
    ],
    "sourceUrl": "https://sanctionssearch.ofac.treas.gov/Details.aspx?id=17022"
  },
  {
    "queryName": "Bank Melli",
    "matchedName": "Bank Melli",
    "matchScore": 100,
    "name": "Bank Melli",
    "entityType": "entity",
    "list": "eu",
    "referenceNumber": "EU.13719.21",
    "programs": ["IRN"],
    "listedOn": "2025-09-29"
  },
  {
    "queryName": "Sberbank",
    "matchedName": "PJSC SBERBANK",
    "matchScore": 100,
    "name": "PUBLIC JOINT STOCK COMPANY SBERBANK OF RUSSIA",
    "entityType": "entity",
    "list": "ofac",
    "listedOn": "2014-09-12"
  }
]
```

With `onlyNew` and Mahan Air added to the names, run `J8Rh4w77IRPfbnD1E` returned 37 matches and the same input run again a minute later (run `9IhMEQiKmDOr7eLmU`) returned 0 rows, since nothing on the lists had changed in between. See "Monitoring and scheduling".

With empty input (run `N58toVUHjTSPkjsJQ`) the actor runs in watch mode and returned the 50 most recently listed or amended records, starting with two entities that OFAC designated under the SDGT program on 2 October 2026.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `names` | array | | Company, vessel or aircraft names to screen, up to 10 000 per run. Leave empty for watch mode. |
| `lists` | array | `["ofac", "eu"]` | `ofac` for the OFAC SDN and Consolidated (non-SDN) lists, `eu` for the EU Consolidated Financial Sanctions List. |
| `matchThreshold` | integer | `85` | Lowest match score to report, 0 to 100. See "How matching works". |
| `includeVesselsAircraft` | boolean | `true` | Also match vessels and aircraft. Set to `false` for companies and organizations only. |
| `maxResults` | integer | `50` | With names: the most matches per name, best first. In watch mode: the number of records. 1 to 1 000. |
| `onlyNew` | boolean | `false` | Return only results that are new or changed since earlier runs with the same input. |

Example input: screen a supplier list against both lists, companies only, with a lower threshold to catch spelling variants.

```json
{
  "names": ["Gazprom Neft", "Mahan Air", "Nordic Paper AB"],
  "lists": ["ofac", "eu"],
  "matchThreshold": 80,
  "includeVesselsAircraft": false
}
```

Example input: watch mode, the 20 newest designations on the EU list.

```json
{
  "lists": ["eu"],
  "maxResults": 20
}
```

### Output

One row per match (or, in watch mode, per listed record):

| Field | Description |
|---|---|
| `queryName` | The name you screened. `null` in watch mode |
| `matchedName` | The listed name or alias that matched best. `null` in watch mode |
| `matchScore` | 0 to 100, see "How matching works". `null` in watch mode |
| `name` | The primary name on the list |
| `entityType` | `entity`, `vessel` or `aircraft` |
| `list` | `ofac` or `eu` |
| `listNames` | The lists the record is on, for example `SDN List` or `Sectoral Sanctions Identifications List` |
| `referenceNumber` | OFAC's entity number or the EU reference number, for example `17022` or `EU.8537.32` |
| `programs` | Sanctions programs, for example `RUSSIA-EO14024` (OFAC) or `UKR` (EU) |
| `listedOn` | Earliest publication date the list gives for the record, `YYYY-MM-DD` |
| `lastChangedOn` | Latest date the list gives for the record (a new listing or an amendment) |
| `aliases` | Other names, including other scripts such as Cyrillic |
| `countries` | ISO 3166 alpha-2 codes from addresses, vessel flags and identifiers |
| `identifiers` | `type`, `value` and `country`: IMO, MMSI, call sign, registration number, tax ID, LEI, SWIFT/BIC, aircraft serial and tail numbers, where the list has them |
| `sourceUrl` | OFAC's page for the record, or the EU regulation (EUR-Lex) behind the latest listing |
| `listPublishedAt` | Publication time of the list file that was read |
| `retrievedAt` | When the actor downloaded the list file |

The run also writes a record `SUMMARY` to the default key-value store with the number of matches per screened name, including names with no match, and the publication date of each list file. Use it as evidence of which names were screened against which list version.

### How matching works

Before comparing, both names are normalized: case, accents and punctuation are removed, and legal forms and filler words (LLC, Ltd, PJSC, OAO, GmbH, AB, SA, "Joint Stock Company", "of", "the") are dropped. Then:

- **100**: the same words, in any order. `Rosneft Oil Co.` and `ROSNEFT OIL COMPANY` score 100.
- **85-99**: every word of the shorter name is in the longer one, for example `Sberbank` and `Sberbank Europe AG`. The more of the longer name is covered, the higher the score. Words of ten letters or more may differ by one letter per ten. Generic words alone (bank, group, trading, international, oil) never count as a match.
- **Below that**: the edit distance similarity of the two names with their words in sorted order, so `Sberbank of Rusia` still matches `Sberbank of Russia`.

The default threshold of 85 finds the listed company and its listed subsidiaries that carry its name. Lower it to 75-80 to catch more transliteration variants, at the cost of more false positives. Each record is reported once per screened name, with the alias that matched best.

### Monitoring and scheduling

Set `onlyNew` to `true` for scheduled runs. The actor then remembers what it has delivered for the same input, in a named key-value store in your Apify account (`nightwave-state-sanctions-entity-screening`, one record per input).

- **With names:** a match is delivered again only when it is new (for example a company newly added to a list) or when the listed record has changed (new alias, program, identifier or listing date). Every screened name is still charged, since it is checked against the full lists on every run.
- **Without names (watch mode):** the first run returns the `maxResults` newest records and remembers every record on the lists as known. Later runs return only records that were added or amended after that.

`onlyNew` and `maxResults` are not part of the remembered input, so you can change them without starting over. Changing any other field starts a fresh state.

Example: a daily check at 07:00 Swedish time for new designations on both lists. In Apify Console, open **Schedules**, create a schedule with the cron expression `0 7 * * *` and add this actor with the input below.

```json
{
  "lists": ["ofac", "eu"],
  "maxResults": 200,
  "onlyNew": true
}
```

The same schedule through the Apify API:

```sh
curl -X POST "https://api.apify.com/v2/schedules?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"name": "daily-sanctions-watch", "cronExpression": "0 7 * * *", "timezone": "Europe/Stockholm", "isEnabled": true, "isExclusive": true,
       "actions": [{"type": "RUN_ACTOR", "actorId": "nightwave-owner~sanctions-entity-screening",
                    "runInput": {"contentType": "application/json; charset=utf-8", "body": "<the input above as a JSON string>"}}]}'
```

To re-screen your own customer or supplier list every day, put the names in `names` with `onlyNew: true`. A run then returns rows only on a day something on the lists changed for one of your names.

### Limits

- **Only the OFAC and EU lists.** The UN Security Council Consolidated List is not included, because its terms of use do not allow commercial reuse (see "Source and license"). The EU implements every UN designation in its own list, so UN listed entities are found through `eu`. UK (OFSI) and other national lists are not covered.
- **Companies, organizations, vessels and aircraft only.** Individuals are never returned, so the actor is not a tool for screening people (PEP or person sanctions checks).
- **Vessels and aircraft come from OFAC.** The EU file lists companies and organizations. Vessels named in EU regulations are not in that file.
- **Name matching only.** The actor does not resolve ownership. A company owned 50 % or more by a listed party can be restricted without being on any list (OFAC's 50 percent rule, and similar EU rules).
- **Each run reads the current files.** About 140 MB is downloaded and parsed per run, which takes 20-30 seconds before matching starts. Matching then takes about 0.06 seconds per name with the default 1 GB of memory, so 1 000 names fit in one run of about two minutes.
- The OFAC SDN file is updated on each designation, the consolidated file and the EU file a few times a month. `listPublishedAt` shows the version that was read.
- A list that cannot be downloaded stops the run with an error after three tries. The actor never screens against an incomplete set of lists.

### Source and license

| List | File used | Terms |
|---|---|---|
| OFAC SDN List and Consolidated (non-SDN) List | `SDN_ENHANCED.XML` and `CONS_ENHANCED.XML` from the [OFAC Sanctions List Service](https://ofac.treasury.gov/sanctions-list-service) | Work of the US Government. 17 U.S.C. § 105: "Copyright protection under this title is not available for any work of the United States Government". |
| EU Consolidated Financial Sanctions List | Financial Sanctions File, XML 1.1, from the public link on [data.europa.eu](https://data.europa.eu/data/datasets/consolidated-list-of-persons-groups-and-entities-subject-to-eu-financial-sanctions) (no account needed) | European Commission reuse notice, Commission Decision 2011/833/EU. The Commission's legal notice: "This means that reuse is allowed, provided appropriate credit is given and changes are indicated." |

Credit: data from the U.S. Department of the Treasury, Office of Foreign Assets Control, and the European Commission (Financial Sanctions Files). Changes made by this actor: records of natural persons are removed, and fields are renamed and normalized (dates as `YYYY-MM-DD`, countries as ISO codes).

The UN list (`scsanctions.un.org`) was reviewed and left out. The UN's terms of use grant permission to download material "for the User's personal, non-commercial use, without any right to resell or redistribute them or to compile or create derivative works therefrom".

This actor is not affiliated with or endorsed by OFAC, the US Treasury or the European Commission. Always check a match against the official source in `sourceUrl`.

### Pricing

Pay per event:

- **0.01 USD per screened name** (event `screened-name`), which is 10 USD per 1 000 names. Charged once per name and run, with or without matches.
- **0.002 USD per record in watch mode** (event `list-record`), which is 2 USD per 1 000 records.

Platform usage is included, so you pay only the event prices above. If you set a maximum cost per run, the actor stops cleanly when it is reached and tells you how many names were screened.

Rows are delivered only after they have been charged. If you set a maximum cost per run (maxTotalChargeUsd), the run stops there and its status message says how many rows were delivered.

### Contact

Built and maintained by Nightwave AB. Questions, bugs and feature requests: kontakt@nightwave.se

### På svenska

Actorn kontrollerar namn på företag, fartyg och flygplan mot OFAC:s sanktionslistor (SDN och Consolidated) och EU:s konsoliderade lista över finansiella sanktioner. Varje träff blir en rad med matchningspoäng, sanktionsprogram, datum för listning, alias, länder och identifierare som IMO-nummer, registreringsnummer och skattenummer.

- Fysiska personer tas bort helt när filerna läses och finns aldrig med i något fält, inte heller bland alias. Inga personuppgifter behandlas.
- Det här är listdata, inte juridisk rådgivning eller compliance-rådgivning. Om en träff är samma företag och vad du får göra avgör du eller din rådgivare.
- Matchning: namnen normaliseras (versaler, accenter, skiljetecken och bolagsformer som AB, LLC och PJSC tas bort). 100 betyder samma ord, 85-99 att alla ord i det kortare namnet finns i det längre, och därunder används redigeringsavstånd. Standardgränsen är 85.
- Utan namn returneras de senast listade eller ändrade posterna (bevakningsläge).
- Bevakning: med `onlyNew` kommer actorn ihåg vad den redan har levererat för samma input (key-value store `nightwave-state-sanctions-entity-screening`) och levererar bara nya eller ändrade träffar. Lägg den på ett dagligt schema under Schedules i Apify.
- FN:s lista ingår inte, eftersom FN:s villkor inte tillåter kommersiell vidareanvändning. EU för in alla FN:s listningar i sin egen lista.
- Källor: OFAC (amerikanskt offentligt verk, fritt att använda) och Europeiska kommissionen (vidareanvändning tillåten enligt beslut 2011/833/EU med angiven källa).
- Pris: 0,01 USD per kontrollerat namn (10 USD per 1 000) och 0,002 USD per post i bevakningsläge. Plattformsanvändningen ingår.
- Kontakt: kontakt@nightwave.se

# Actor input Schema

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

Names to screen, one per line, for example \["Rosneft", "Bank Melli", "Sberbank"]. Each name is matched against every name and alias on the selected lists. Leave empty to get the most recently listed or amended entities instead (watch mode). Up to 10 000 names per run.

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

Which lists to use: ofac (the SDN list and the Consolidated non-SDN list of the US Treasury) and eu (the EU Consolidated Financial Sanctions List). For example \["ofac", "eu"]. Defaults to both.

## `matchThreshold` (type: `integer`):

Lowest name similarity to report, 0 to 100, for example 85. 100 means the same name after removing legal forms (LLC, PJSC, GmbH), case, accents and punctuation. Lower it to 75 to catch more spelling variants, raise it to 95 for fewer false positives. Defaults to 85.

## `includeVesselsAircraft` (type: `boolean`):

Also match vessel and aircraft names (OFAC lists them with IMO numbers, call signs and serial numbers), for example true. Set to false to screen against companies and organizations only. Defaults to true.

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

With names: the maximum number of matches returned per name, best first. Without names (watch mode): the number of recently listed records returned. For example 50. 1 to 1 000, defaults to 50.

## `onlyNew` (type: `boolean`):

For scheduled runs. When true, results that an earlier run with the same input already delivered are skipped, so a daily run returns only new matches, records added to a list or records whose listing was amended. Defaults to false.

## Actor input object example

```json
{
  "names": [
    "Rosneft",
    "Bank Melli",
    "Sberbank"
  ],
  "lists": [
    "ofac",
    "eu"
  ],
  "matchThreshold": 85,
  "includeVesselsAircraft": true,
  "maxResults": 50,
  "onlyNew": true
}
```

# Actor output Schema

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

All rows produced by the run, as JSON. Open in Apify Console or download via the dataset API.

# 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": [
        "Rosneft",
        "Bank Melli",
        "Sberbank"
    ],
    "lists": [
        "ofac",
        "eu"
    ],
    "matchThreshold": 85,
    "includeVesselsAircraft": true,
    "maxResults": 50,
    "onlyNew": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("nightwave-owner/sanctions-entity-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": [
        "Rosneft",
        "Bank Melli",
        "Sberbank",
    ],
    "lists": [
        "ofac",
        "eu",
    ],
    "matchThreshold": 85,
    "includeVesselsAircraft": True,
    "maxResults": 50,
    "onlyNew": False,
}

# Run the Actor and wait for it to finish
run = client.actor("nightwave-owner/sanctions-entity-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": [
    "Rosneft",
    "Bank Melli",
    "Sberbank"
  ],
  "lists": [
    "ofac",
    "eu"
  ],
  "matchThreshold": 85,
  "includeVesselsAircraft": true,
  "maxResults": 50,
  "onlyNew": false
}' |
apify call nightwave-owner/sanctions-entity-screening --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nightwave-owner/sanctions-entity-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/eVFWxs2CNppnLYHUe/builds/CzkIjeevtjNRbhkYr/openapi.json
