# Sanctions Check — OFAC SDN Screening (`mfapitools/sanctions-check`) Actor

Search the OFAC Specially Designated Nationals (SDN) sanctions list by name, alias, address, or ID. Token-indexed for accurate partial-name matches across 19K entries. Pay-per-event: charged per query plus per match found, no subscription. Built for KYC and supplier screening.

- **URL**: https://apify.com/mfapitools/sanctions-check.md
- **Developed by:** [Mariano Ferreras](https://apify.com/mfapitools) (community)
- **Categories:** Automation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 name screeneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Sanctions Check — OFAC SDN Screening, Pay-Per-Query

> **Pay-per-event — zero monthly fee, zero subscription.** You pay for each query executed and each match found, nothing else. No seat licenses, no per-record data fees, no idle-month charges.

***

### What it does

This actor searches the U.S. Treasury OFAC **Specially Designated Nationals (SDN) list** — the primary sanctions screening list used for U.S. compliance — by name, alias, address, or identifier. It has two modes:

- **`refresh`** — downloads the current OFAC SDN XML (~27 MB, ~19,000 entries) and builds a **token-based inverted index** in Apify's Key-Value Store: every name, alias, address city/country, and ID number is normalized and split into searchable word tokens, so partial-name queries resolve correctly against the full dataset instead of requiring an exact match.
- **`query`** — searches that index by name or identifier with substring/fuzzy scoring, and returns every matching SDN record: entity name, type (Individual/Entity/Vessel/Aircraft), sanctions programs, aliases, addresses, ID numbers, and remarks.

If you're looking for an **OFAC SDN API** without building your own XML parser and refresh pipeline, this gives you structured JSON matches for any name query, on demand or scheduled.

***

### Use cases

- **KYC / customer onboarding** — screen a new customer, counterparty, or beneficial owner's name against the SDN list before account opening.
- **Supplier / vendor due diligence** — check suppliers, contractors, or M\&A targets for sanctions exposure before signing.
- **AI agent compliance tool** — give an LLM agent a callable sanctions-screening function via the Apify MCP server (see below).
- **Journalist / researcher due diligence** — quick name checks against public sanctions data.
- **LatAm due-diligence profiles** — pair with `cuit-lookup` (AFIP/CUIT identity data) and `licitaciones-feed` (public tender awards) to build a combined counterparty profile.

***

### Input

```json
{ "mode": "refresh" }
```

```json
{ "mode": "query", "query": "MADURO", "searchType": "all", "maxResults": 10, "minScore": 0.5 }
```

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | `refresh` | `query` | `query` | `refresh` downloads and rebuilds the index (run this at least once before querying). `query` searches the existing index. |
| `query` | string | — | Name or identifier to search (case-insensitive). Required in `query` mode. |
| `searchType` | `name` | `all` | `all` | `name` = match entity name only. `all` = also match addresses, IDs, and aliases. |
| `maxResults` | integer | 20 | Maximum matches to return (1–100). |
| `minScore` | number | 0.5 | Fuzzy match threshold: `1.0` = exact, `0.7` = substring, `0.5` = partial word. |

**Workflow:** run once with `{ "mode": "refresh" }` (~30–45s, downloads + indexes the full list), then run `{ "mode": "query", "query": "..." }` for each name you want to screen. Schedule `refresh` daily (or at whatever cadence matches your compliance policy) to keep the index current — see [Freshness](#honest-limitations) below.

***

### Output

Each match is one dataset row. Example — querying `"MADURO"` against a freshly refreshed index:

```json
{
  "query": "MADURO",
  "name": "MADURO MOROS, Nicolas",
  "sdnType": "Individual",
  "programs": ["VENEZUELA", "IRAN-CON-ARMS-EO"],
  "remarks": "President of Venezuela.",
  "addresses": [
    { "city": "Caracas", "country": "Venezuela", "address": "" }
  ],
  "ids": [],
  "aliases": [
    { "name": "MADURO MOROS, Nicolas Maduro", "type": "aka" }
  ],
  "source": "OFAC-SDN",
  "sourceUrl": "https://sanctionslistservice.ofac.treas.gov/api/publicationpreview/exports/sdn.xml?uid=22790",
  "lastUpdated": "2026-07-24T00:00:00.000Z",
  "searchedAt": "2026-07-24T13:05:00.000Z"
}
```

| Field | Type | Notes |
|---|---|---|
| `query` | string | The search term you submitted |
| `name` | string | Matched entity's display name (`lastName, firstName` for individuals) |
| `sdnType` | string | `Individual`, `Entity`, `Vessel`, or `Aircraft` |
| `programs` | string\[] | Sanctions program codes (e.g. `VENEZUELA`, `SDGT`, `KINGPIN`) |
| `remarks` | string|null | OFAC's free-text remarks field, when present |
| `addresses` | array | City/country/address strings from the record |
| `ids` | array | Passport / national ID / other identifiers, with type and issuing country |
| `aliases` | array | Known AKA names, with alias type |
| `source` | string | Always `"OFAC-SDN"` in v1 |
| `sourceUrl` | string | Direct link back to the source dataset for this record |
| `lastUpdated` | string | Timestamp of the OFAC file this index was built from |
| `searchedAt` | string | Timestamp this query ran |

A query that finds zero matches still runs and charges the `sanction-screened` event (a clean "not found" is a valid compliance result, not an error) — it just pushes no rows and charges no `sanction-match` events.

***

### Pricing (Pay-Per-Event)

| Event | What triggers it | Price |
|---|---|---|
| `sanction-screened` | Per query execution (`mode: "query"`), even if zero matches | $0.003 |
| `sanction-match` | Per match found and returned | $0.01 |
| Actor start | One-time per run. Apify charges this event once per GB of run memory (minimum 1); this actor's default memory is 2048 MB (2 GB), so a standard run charges 2 actor-start events at $0.0005 each | $0.001 total/run |

Each `query` is one search term per run (the input takes a single `query` string, not a batch array) — screening multiple names means multiple runs, each with its own actor-start + `sanction-screened` charge.

**Real-world cost examples:**

| Scenario | Runs | Matches | Est. cost |
|---|---|---|---|
| Clean name, no hits (typical KYC check) | 1 | 0 | ~$0.004 |
| Common surname, several hits | 1 | 4 | ~$0.044 |
| Bulk screen, 50 names, ~10% hit rate | 50 | 5 | ~$0.25 |

**Measured platform cost:** ~$0.0004–0.0005 per query in Apify compute-unit terms (validated build 0.1.4, platform runs `ZmZvQSUWXes0L1dH8` and `4dMt9h3CY1CXRZZxn`) — at $0.003/query that's roughly 83–87% gross margin before the platform's own PPE cut. `refresh` runs (index rebuild) are not separately billed beyond the actor-start charge; they cost the developer ~$0.03/run in compute (~$1/month at daily cadence) and are intended to run on a schedule, not per-customer-query.

***

### Use with AI agents

This actor is available as a tool for AI assistants via the Apify MCP server. Agents can call it to **screen a name against the OFAC SDN list** directly — no Apify console needed, just a single function call.

#### MCP setup

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.apify.com/?tools=mfapitools/sanctions-check",
        "--header",
        "Authorization: Bearer <YOUR_API_TOKEN>"
      ]
    }
  }
}
```

#### Example agent prompts

- "Check if 'Nicolas Maduro' appears on the OFAC SDN sanctions list"
- "Screen this list of 20 supplier names against U.S. sanctions before we onboard them"
- "Is this company or any of its known aliases sanctioned?"

The actor returns structured JSON with the same fields shown above, so an agent can reason over match strength (`programs`, `aliases`, `remarks`) without parsing raw XML.

***

### Honest limitations

- **NOT a substitute for professional compliance software.** This is a name-search tool over a public dataset. It does not perform risk scoring, PEP screening, adverse media checks, or transactional/payment screening.
- **OFAC SDN only in v1.** UN, EU, and UK sanctions lists are not covered. If your compliance policy requires multi-list screening, this covers only the U.S. SDN list.
- **Query candidate retrieval requires a whole matching word (token), not an arbitrary substring.** A query for `"MADURO"` finds records because `MADURO` is indexed as a complete token. A query for `"MADUR"` (a sub-word fragment that is never itself a complete word in the index) will **not** retrieve any candidates — there's no entry for the fragment "MADUR", only for the full word "MADURO". Once a candidate record IS retrieved via a full-token hit, the existing substring/fuzzy scoring still evaluates the WHOLE query string against that record's fields for ranking — so `"MADURO MOR"` still scores well against `"MADURO MOROS, Nicolas"` (retrieved via the "MADURO" token, then scored as a partial-word match). Very short typos inside a single word (e.g. `"MADURRO"`) are not corrected — there is no edit-distance/phonetic matching.
- **Multi-word queries use UNION, not intersection, of per-word candidates** — chosen deliberately to favor recall for a screening tool (a record shouldn't be silently dropped just because it only matched one of several query words). This means a two-word query where only one word is real can still surface a match; the score/ranking (not the candidate set) is what tells you how strong the match is.
- **Freshness depends on how often you run `refresh`.** OFAC updates the SDN list as needed (sometimes daily). This actor does not auto-refresh — schedule `refresh` runs at a cadence that matches your compliance requirements, and treat `lastUpdated` on each result as the authoritative "as of" date.
- **~19K entries — a small, focused dataset.** The value here is search/index convenience and structured output over the SDN list, not data volume.
- **No fuzzy phonetic matching (e.g. Soundex).** Name variant spelling that shares no common whole-word token with the indexed name will not be found.

***

### Compliance

- **Public-domain government data.** The OFAC SDN list is published by the U.S. Department of the Treasury and is explicitly public domain — no license, login, or API key is required to access it.
- **Source:** `https://sanctionslistservice.ofac.treas.gov/api/publicationpreview/exports/sdn.xml`, the same file OFAC's own compliance tools and every commercial screening vendor ultimately index.
- **Update cadence:** OFAC republishes the SDN list as needed (frequently, sometimes daily, whenever designations change). This actor reflects whatever version was present the last time `refresh` was run — it does not poll OFAC continuously. Run `refresh` on a schedule matching your risk tolerance.
- **No sensitive personal data beyond the source record.** Output fields (name, aliases, addresses, ID numbers, remarks) mirror exactly what OFAC publishes in the SDN file — nothing is enriched, inferred, or sourced elsewhere.

***

### Changelog

See [CHANGELOG.md](./CHANGELOG.md).

# Actor input Schema

## `mode` (type: `string`):

`refresh` downloads and indexes the latest OFAC SDN list (must be run at least once before queries). `query` (default) searches the stored index for matches.

## `query` (type: `string`):

Name or identifier to search. Case-insensitive substring match. Only used in `query` mode.

## `searchType` (type: `string`):

`name` = match against sdnEntry lastName + firstName only. `all` = also match addresses, IDs, and aliases.

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

Maximum number of matches to return.

## `minScore` (type: `number`):

Fuzzy match threshold (0 = everything, 1 = exact only). Scores: exact name=1.0, substring=0.7, partial word=0.5.

## Actor input object example

```json
{
  "mode": "query",
  "query": "Iran",
  "searchType": "all",
  "maxResults": 20,
  "minScore": 0.5
}
```

# 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 = {
    "mode": "query",
    "query": "Iran",
    "searchType": "all"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mfapitools/sanctions-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 = {
    "mode": "query",
    "query": "Iran",
    "searchType": "all",
}

# Run the Actor and wait for it to finish
run = client.actor("mfapitools/sanctions-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 '{
  "mode": "query",
  "query": "Iran",
  "searchType": "all"
}' |
apify call mfapitools/sanctions-check --silent --output-dataset

```

## MCP server setup

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