# AML Screening API - Sanctions, PEP, Adverse Media, Vessels (`nabeelbaghoor/aml-watchlist-screening-api`) Actor

AML watchlist screening with the Castellum.AI API: batch screen people, companies, vessels and aircraft against sanctions, PEP, law enforcement, export control and adverse media lists, run freeform screens, pull sanctioned bank ID and crypto wallet reports and list sources. Bring your own key.

- **URL**: https://apify.com/nabeelbaghoor/aml-watchlist-screening-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 screening hit returneds

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

## AML Screening API - Sanctions, PEP, Adverse Media, Vessels

Batch screen people, companies, vessels and aircraft against sanctions, PEP, law enforcement, export control and adverse media lists through the Castellum.AI API with your own credentials, and get every hit as a clean dataset row.

Works with the Castellum.AI REST API (api.castellum.ai, documented in its public OpenAPI reference on SwaggerHub): POST /screen_entities to batch screen names, POST /screen_entity/freeform to screen an unstructured description, POST /reports for the sanctioned bank ID, enriched bank ID and crypto sanctions reports, and GET /list_sources for the list source catalogue. Each hit becomes one row with the screened name, match score, matched list entry, entity type, list source, category and lists, aliases, birth dates, citizenships, countries, addresses, ID numbers, relationships and, for adverse media, the article title, URL and date, plus the full Castellum.AI record. Download as JSON, CSV or Excel, schedule it, or feed it into a KYC, KYB, onboarding or payment screening workflow or an AI agent. Bring your own API key.

BYOK, not live-tested: every route, body field, filter and response field comes from Castellum.AI's published API reference and support centre, and every route was confirmed to exist on api.castellum.ai, but the actor has not been run against a live Castellum.AI account.

### What it does

- **Screen names**: batch screening of up to 1,000 names a run, 50 to a request, against the categories you pick (Sanctions, PEPs, Law Enforcement, Export Control, Delisted, Contract Debarment, Elevated Risk, Regulatory Enforcement, High-Risk Ownership, Adverse Media) and any exact list sources such as US OFAC SDN. Filter by entity type and country, set the minimum match score, exclude weak aliases, cap hits per name, or run a delta screen of entries updated since a date. One row per hit.
- **Freeform screening**: send a description such as "John Smith born January 15 1980 living in New York" and Castellum.AI extracts the name and attributes before screening.
- **Bank ID and crypto sanctions report**: sanctioned bank identifiers, enriched bank IDs and sanctioned crypto wallet addresses, by list source and since a date.
- **List sources**: every list source your credentials can screen against, with category, lists, authority, issuing country and last update, so you can scope screens exactly.
- **Read only**: no monitor, notifier or custom list route is called; nothing in your Castellum.AI account changes.
- **Pay only for results**: a name with no hits, an empty report or a refused request gives one free row that says why.

### Important: each name is a billable Castellum.AI query

Castellum.AI counts every name in a batch as one API call on your contract. The run sends only as many names as `maxResults` can still return, so no query is spent on rows that would be cut. Screening the same list twice runs twice the queries.

### How to get credentials

Castellum.AI production API access is part of a Castellum.AI contract: the sales team issues a client id and client secret (a free Platform account lets you try the watchlist data in the web app first). Paste the client id into Client ID and the secret into Client secret, or set the DATA_API_CLIENT_ID and DATA_API_KEY secrets. The actor exchanges them at auth.castellum.ai for a 24 hour access token once per run and reuses it. You can also paste a ready access token into Client secret and leave Client ID empty. Secrets go only to Castellum.AI and are never logged. Missing or rejected credentials end the run cleanly with a status message.

### Input

| Field | Modes | Description |
| --- | --- | --- |
| `mode` | all | `screen`, `freeform`, `report` or `listSources` |
| `names` | screen | Names to screen, one per line (up to 1,000 a run) |
| `freeformTexts` | freeform | Unstructured descriptions, one per line (up to 100 a run) |
| `categories` | screen, freeform | Sanctions, PEPs, Law Enforcement, Export Control, Delisted, Contract Debarment, Elevated Risk, Regulatory Enforcement, High-Risk Ownership, Adverse Media |
| `listSourceNames` | screen, freeform, report | Exact list source names, such as US OFAC SDN |
| `entityTypes` | screen | Individual, Entity, Vessel, Aircraft, Location, Media, Security, Shipment |
| `countries` | screen | ISO alpha-2 country codes |
| `minScore` | screen, freeform | Minimum match score, 0 to 1 (default 0.85) |
| `excludeWeakAkas` | screen | Leave out weak alias matches |
| `hitsPerName` | screen | Hits per name, 1 to 200 (default 10) |
| `asOf` | screen, report | Updated since date for a delta screen or report |
| `flattenCatchall` | screen | Flat catch-all source data |
| `reportTypes` | report | bank_ids, crypto_sanctions, enriched_bank_ids |
| `maxResults` | all | Row ceiling for the run (default 100) |
| `requestsPerMinute` | all | Pacing, default 60 |
| `clientId`, `apiKey` | all | Your Castellum.AI client id and client secret (or an access token in `apiKey`) |

At least one category or list source is required to screen. An input set for a mode that does not take it is left off with a warning, never sent.

Example: screen two companies against sanctions and export control lists only.

```json
{
    "mode": "screen",
    "names": ["Gazprom", "Rosneft"],
    "categories": ["Sanctions", "Export Control"],
    "entityTypes": ["Entity"],
    "minScore": 0.85,
    "maxResults": 50
}
```

### Example output

An illustrative screening hit, shaped like the documented POST /screen_entities response (values are an example). The full hit is kept under `record`.

```json
{
    "mode": "screen",
    "modeLabel": "Screen names",
    "recordType": "hit",
    "input": "Peter Griffin",
    "found": true,
    "queryName": "Peter Griffin",
    "totalHits": 1,
    "score": 1,
    "entryName": "Peter Griffin",
    "entityType": "Individual",
    "listSource": "OFAC SDN",
    "listSourceCategory": "Sanctions",
    "lists": "NPWMD",
    "aliases": null,
    "birthDates": "1935-09-09",
    "citizenships": "GB",
    "countries": "GB",
    "placesOfBirth": "Oxford, United Kingdom",
    "idNumbers": "Passport: B401584; Passport: B109455",
    "providerId": "11360",
    "lastUpdated": null,
    "articleTitle": null,
    "articleUrl": null,
    "matchedName": null,
    "note": null,
    "record": { "score": 1, "list_entry": { "provider_id": "11360", "type": { "id": 1, "name": "Individual" } } }
}
```

### FAQ

#### What is the Castellum.AI API?

Castellum.AI is a financial crime compliance company. Its REST API screens names against watchlist data it collects and maintains in house: global sanctions lists (OFAC, UK, EU, UN and more), PEPs, law enforcement, export control, contract debarment, delisted, elevated risk, regulatory enforcement, high-risk ownership and adverse media, covering individuals, entities, vessels, aircraft, locations and securities.

#### Do I need my own Castellum.AI credentials?

Yes. This is a bring-your-own-key actor: it calls Castellum.AI with your client id and secret, under your contract and its terms. Missing or rejected credentials end the run cleanly with a status message, and nothing is charged.

#### Does screening use up my Castellum.AI queries?

Yes. Castellum.AI bills each name query as one API call, even inside a batch. The list source catalogue and the reports are one request each.

#### How do I reduce false positives?

Raise the minimum match score, set the entity type, add country codes, exclude weak aliases, and scope the screen to the categories or list sources you need. Use the Updated since date to rescreen only against entries that changed.

#### Can I screen for adverse media only?

Yes. Pick only the Adverse Media category. If you also set entity types, include Media, because adverse media hits are articles with the entity type Media.

#### Can I screen crypto wallets or bank identifiers?

The Bank ID and crypto sanctions report mode returns the sanctioned bank IDs, enriched bank IDs and crypto wallet addresses Castellum.AI extracts from list sources, which you can match against your own records.

#### What do I pay?

Pay per result: 0.015 USD per screening hit, 0.01 USD per report list entry and 0.005 USD per list source. Names with no hits, empty reports and refused requests are never charged. Your Castellum.AI contract is billed separately by Castellum.AI.

### Pricing

| Event | Price | Charged for |
| --- | --- | --- |
| `screening-hit` | 0.015 USD | Each sanctions, PEP, watchlist or adverse media hit from Screen names or Freeform screening |
| `watchlist-entry` | 0.01 USD | Each list entry in a bank ID or crypto sanctions report |
| `list-source` | 0.005 USD | Each list source in the catalogue |

### Keyword map

AML screening API, sanctions screening API, PEP screening API, adverse media screening, watchlist screening, OFAC SDN screening, KYC screening, KYB screening, vessel sanctions screening, aircraft sanctions screening, export control screening, crypto wallet sanctions list, sanctioned bank BIC list, batch name screening, Castellum.AI API, financial crime compliance data for AI agents.

### Terms and data use

Castellum.AI data is licensed under your agreement with Castellum.AI (the User Agreement, Version 1.3 of 1 March 2025, plus your API contract). The User Agreement licenses the Product, which includes its watchlist data, to you solely as an end-user and forbids reproducing, sublicensing, selling or transferring it to any third party without written agreement (s.4). It also forbids copying the Product without consent (s.7.2), running automated queries unless agreed in a separate agreement, which your API contract is (s.7.5), keeps Product details confidential (s.8), and forbids giving access to parties Castellum.AI has not authorised (s.15). The terms set no limit on how long you keep your screening results. Run this actor with your own credentials, screen your own customers and counterparties, keep the results for your own compliance records, and do not share or resell them. This actor is not affiliated with or endorsed by Castellum.AI.

# Actor input Schema

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

Screen names batch screens people, companies, vessels and aircraft against the categories and list sources you pick, one row per hit. Freeform screening sends an unstructured description (name, birth date, place) that Castellum.AI parses before screening. Bank ID and crypto sanctions report lists sanctioned bank identifiers and crypto wallet addresses. List sources returns every list source your credentials can screen against, with exact names to use in the list source input.

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

Screen names only. One person, company, vessel or aircraft name per line, up to 1,000 a run. Each name is one billable query on your Castellum.AI contract.

## `freeformTexts` (type: `array`):

Freeform screening only. One unstructured description per line, up to 100 a run, such as John Smith born January 15 1980 living in New York.

## `categories` (type: `array`):

Screen names and Freeform screening. The list source categories to screen against. At least one category or one list source is required. Adverse media hits have the entity type Media.

## `listSourceNames` (type: `array`):

Screen names, Freeform screening and the report. Optional exact list source names, one per line, such as US OFAC SDN. Run the List sources mode to see the names your credentials cover. Castellum.AI recommends categories or list sources over single lists, because lists change often.

## `entityTypes` (type: `array`):

Screen names only. Leave empty to match every type.

## `countries` (type: `array`):

Screen names only. Optional ISO 3166-1 alpha-2 country codes, one per line, such as RU or IR.

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

Screen names and Freeform screening. Hits scoring below this (0 to 1) are not returned. Castellum.AI's own examples use 0.8 to 0.85.

## `excludeWeakAkas` (type: `boolean`):

Screen names only. Leaves out matches on aliases the source marks as weak, which cuts false positives.

## `hitsPerName` (type: `integer`):

Screen names only. The most hits returned for each name (page size, 1 to 200).

## `asOf` (type: `string`):

Screen names and the report. Optional YYYY-MM-DD or ISO date-time. Runs a delta screen: only list entries updated on or after this date are returned.

## `flattenCatchall` (type: `boolean`):

Screen names only. Asks Castellum.AI to return the catch-all source data on each list entry in a flat format.

## `reportTypes` (type: `array`):

Bank ID and crypto sanctions report only. Narrow it with list sources and the updated since date.

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

The most rows this run returns and charges for. Only this many names or descriptions are sent to Castellum.AI, so no query is spent on rows that would be cut.

## `requestsPerMinute` (type: `integer`):

Pacing. A rate limit answer is waited out and retried; a used up allowance ends the run.

## `clientId` (type: `string`):

Your own Castellum.AI production API client id, issued by the Castellum.AI sales team. Stored as a secret. You can instead set the DATA_API_CLIENT_ID environment secret.

## `apiKey` (type: `string`):

The client secret that goes with the client id. The pair is exchanged at auth.castellum.ai for an access token, reused for the whole run. You can paste a ready access token here instead and leave the client id empty. Stored as a secret, sent only to Castellum.AI, never logged. You can instead set the DATA_API_KEY environment secret.

## Actor input object example

```json
{
  "mode": "screen",
  "names": [
    "Gazprom",
    "Alisher Usmanov"
  ],
  "categories": [
    "Sanctions",
    "PEPs"
  ],
  "minScore": 0.85,
  "excludeWeakAkas": false,
  "hitsPerName": 10,
  "flattenCatchall": false,
  "reportTypes": [
    "crypto_sanctions"
  ],
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `records` (type: `string`):

One row per hit, report entry or list source returned for the chosen mode.

# 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": [
        "Gazprom",
        "Alisher Usmanov"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/aml-watchlist-screening-api").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": [
        "Gazprom",
        "Alisher Usmanov",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/aml-watchlist-screening-api").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": [
    "Gazprom",
    "Alisher Usmanov"
  ]
}' |
apify call nabeelbaghoor/aml-watchlist-screening-api --silent --output-dataset

```

## MCP server setup

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

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/6huR0uMzOH1EREgvC/builds/A7RWHxzp7rG9oXprW/openapi.json
