# UK Sanctions List Change Monitor (FCDO UKSL) (`plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch`) Actor

Track changes to the UK Sanctions List entries you are exposed to: watch FCDO UKSL Unique IDs and get DELISTED, VARIATION (regime, sanctions, names) and DESIGNATED events from the official UKSL file. For compliance teams holding frozen assets or acting under OFSI licences. No API key.

- **URL**: https://apify.com/plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch.md
- **Developed by:** [Daniel Witney](https://apify.com/plym-actor-factory) (community)
- **Categories:** AI, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 uksl designation event delivereds

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

## UK Sanctions List Change Monitor (FCDO UKSL)

**Track changes to the UK Sanctions List entries you are exposed to.** Watch FCDO **UK Sanctions List (UKSL)** Unique IDs (or OFSI Group IDs) and get a typed **DELISTED / VARIATION / DESIGNATED** event when the official UKSL CSV/XML changes for an entry on your book: a delisting, a change of regime or sanctions imposed, new names or aliases, or a watched ID (re)appearing on the list. Built for **compliance teams and MLROs** that hold frozen assets, act under OFSI licences, or monitor listed counterparties. Official FCDO file, **no API key**. **Not** a new-customer screening tool (name watch is an imperfect exact-normalised match; see *First run vs later runs*), not a multi-list OFAC / EU / UN screener, and not an HTML scrape.

**Quick start:** put the UKSL Unique IDs you watch in *UKSL Unique IDs to watch* → set **Source mode = live** (no key needed) → save as a Task and schedule it daily. The first live run records a baseline (no charge); later runs deliver an event only when a watched entry is delisted, varied or (re)designated. Clicking *Start* with the defaults runs a free health check only.

### Who this is for

- Banks, payment firms and insurers **holding frozen funds, accounts or policies** of designated persons, who must act when an entry is varied or delisted.
- Law firms and professional advisers **acting under OFSI licences** for listed clients or counterparties.
- Compliance teams that have matched specific UKSL entries and need an **audit trail of changes** to those entries (with FCDO `as_of` and `source_url`).

Not a replacement for customer screening: to find out whether a customer is *newly* designated, use a screening tool. This Actor watches entries you already know.

#### Typical workflow

1. List the UKSL Unique IDs you are exposed to (e.g. `AFG0001`); optionally OFSI Group IDs.
2. Run once with `sourceMode: "live"` — this **records a baseline** of each watched entry (no charge, no events).
3. Save as a Task and **schedule daily** (FCDO publishes on a daily-class cadence).
4. Each later run downloads the official UKSL file, **compares it with the stored baseline** and delivers `UKSL_DELISTED`, `UKSL_VARIATION` or `UKSL_DESIGNATED` only for entries on your book. Route them to case management or email via Apify integrations and keep `as_of` / `source_url` as evidence.

#### First run vs later runs

**Run 1 (live) is a baseline:** it stores the current UKSL state of every watched entry and emits no events. **Run 2 onwards** compares against that baseline, so keep the same Task (its key-value store holds the checkpoint). A watched Unique ID that is absent at baseline and later appears is reported as `UKSL_DESIGNATED`. **Names:** on a name's first run, entries already listed under that name are stored as a baseline (no events). If the watched name later appears under a **new** Unique ID, that is reported as `UKSL_DESIGNATED` with `match_method: "name"`. A name added to the watchlist later gets its own silent baseline run first. Name matching is exact after normalisation (case, accents, punctuation), so spelling variants and transliterations are not caught; prefer Unique ID.

#### Example output (SAMPLE)

> SAMPLE — illustrative values in the real output shape; `XXX0000` / `EXAMPLE HOLDINGS LTD` / `Example Regime` are placeholders, not a real UKSL entry or a real delisting.

```json
{
  "unique_id": "XXX0000",
  "ofsi_group_id": "00000",
  "primary_name": "EXAMPLE HOLDINGS LTD",
  "event_type": "UKSL_DELISTED",
  "event_subtype": "ABSENT_FROM_LIST",
  "changed_fields": [
    "missing"
  ],
  "previous_value": "designated",
  "new_value": "absent",
  "effective_date": "2026-09-17",
  "as_of": "2026-09-17",
  "evidence": {
    "previous_regime": "Example Regime",
    "previous_sanctions": "Asset freeze",
    "previous_names": [
      "EXAMPLE HOLDINGS LTD"
    ],
    "previous_ofsi_group_id": "00000"
  },
  "summary": "Unique ID XXX0000 (EXAMPLE HOLDINGS LTD) no longer present on FCDO UK Sanctions List — DELISTED / revoked for the watched book.",
  "source_url": "https://sanctionslist.fcdo.gov.uk/docs/UK-Sanctions-List.csv",
  "event_uid": "<md5>",
  "billable": true,
  "match_method": "unique_id"
}
```

**What you get per $0.02 event:** one deduplicated delisting, material variation or designation for one entry on your watchlist (one dataset row like the sample above). Baselines, quiet runs, duplicates and entries not on your book are never charged.

### What this Actor does

1. Takes your watchlist (`uniqueIds` preferred; optional `ofsiGroupIds` / `names`).
2. In **live** mode, downloads the official FCDO machine-readable file once:
   - `https://sanctionslist.fcdo.gov.uk/docs/UK-Sanctions-List.csv` (primary)
   - `.xml` reachable when `preferFormat=xml` (aggregation still uses Unique-ID CSV grain)
3. Matches watched keys only (Unique ID / OFSI Group ID / imperfect name).
4. Diffs against a durable Key-Value Store checkpoint.
5. Pushes **only** material designation events for counterparties on your book (PPE), plus one non-billable `RUN_STATUS` health row.

**First observation of a Unique ID is baseline only** (no charge). Quiet days ≈ $0 product charges.

### Coverage honesty (read this)

| In scope | Out of scope |
|----------|----------------|
| Official FCDO **UK Sanctions List (UKSL)** CSV/XML | **OFAC SDN**, EU consolidated, UN list as primary |
| Post-**2026-01-28** single UK designations list | Multi-jurisdiction “screen once” screener SKU |
| Unique ID / OFSI Group ID portfolio watch | Nationwide dump of all designations billed as rows |
| Material VARIATION on regime / sanctions / names | HTML scrape of search-uk-sanctions-list UI |
| Daily-class FCDO file publish cadence | Intra-day FCDO email alert replacement |

**Name match is imperfect** (aliases, non-Latin script, transliteration). Prefer **Unique ID**. Cite FCDO + retrieval date (`as_of` / `fetchedAt`). Do not use Crown emblems.

### Input

| Field | Notes |
|-------|--------|
| `uniqueIds` | Preferred FCDO Unique IDs (`AFG0001`). |
| `ofsiGroupIds` | Optional legacy OFSI Group IDs. |
| `names` | Optional normalised names — **imperfect**; prefer Unique ID. |
| `signalGroups` | `designation` (DESIGNATED/DELISTED), `variation` (material field changes). |
| `sourceMode` | `fixture` (default — Store health) or `live` (FCDO CSV/XML; **no API key**). |
| `emitFixtureDemoEvents` | Default **false**. Only enable for local demos; Store auto-tests must stay false. |
| `preferFormat` | `csv` (default) or `xml`. |

### Output events (billable)

| `event_type` | When |
|--------------|------|
| `UKSL_DESIGNATED` | Watched identity absent → present on UKSL |
| `UKSL_DELISTED` | Watched identity present → absent on UKSL |
| `UKSL_VARIATION` | Material regime / sanctions / names / designation-type change while still listed |

Every successful run also pushes **`RUN_STATUS`** with `billable: false` (`FIXTURE_HEALTH` / `MONITOR_IDLE` / `CATCHUP_COMPLETE` / `HEALTH_OK`).

### Pricing (PPE)

| Event | Price |
|-------|-------|
| `uksl-designation-event-delivered` | **$0.02** ($20 / 1k) |
| `apify-actor-start` | **$0.00005** |

You pay when a counterparty **on your watchlist** has a material designation/delisting/variation and we deliver it. Not charged: baselines, quiet books, duplicates, invalids, `RUN_STATUS`, fixture health.

### Modes

- **Fixture (default):** no network; emits `RUN_STATUS` / `FIXTURE_HEALTH` only → **0** product charges (Store daily auto-test safe).
- **Live:** official FCDO UKSL CSV/XML — **no key required**.

### Legal

UKSL published by FCDO for public reuse. Cite FCDO + retrieval date. Unique ID preferred; name match imperfect. Not a multi-jurisdiction screener. Do not present as ComplyAdvantage / World-Check / OpenSanctions paid coverage.

### Not this product

- `neuton/uk-sanctions-list-scraper`, `parseforge/uk-hmt-sanctions-scraper`, `muhammadafzal/uk-hm-treasury-sanctions-scraper` — dumps / lookups (different job).
- `mooseandraven/ofac-sanctions-screening-suite`, `regdata/adverse-media-screener`, OpenSanctions search Actors — multi-list / point-in-time screeners.
- `nexgenwatch/eu-consolidated-sanctions-change-watch` — **EU** whole-list change, not UKSL portfolio book.
- HTML scrape of FCDO search UI; OFAC SDN scrape; ComplyAdvantage / OpenSanctions paid API as primary.

### Example input (live baseline)

```json
{
  "uniqueIds": ["AFG0001", "AFG0003"],
  "ofsiGroupIds": [],
  "names": [],
  "sourceMode": "live",
  "signalGroups": ["designation", "variation"],
  "preferFormat": "csv",
  "resumeFromCheckpoint": true,
  "emitFixtureDemoEvents": false,
  "maxRunSeconds": 120
}
```

First live run stores baselines only. Subsequent scheduled runs emit events when designation state or material fields flip on the book.

# Actor input Schema

## `uniqueIds` (type: `array`):

FCDO UKSL Unique IDs to monitor (e.g. AFG0001). Preferred match key. Invalid/blank entries are skipped with no charge. Not a nationwide dump — only IDs on the book are evaluated.

## `ofsiGroupIds` (type: `array`):

Optional legacy OFSI Group ID keys. Resolved to Unique IDs present on the current UKSL snapshot.

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

Optional counterparty names when Unique ID is unknown. Matching is imperfect (aliases / non-Latin script / punctuation). Prefer Unique ID. Matched entities are keyed by Unique ID for checkpoints. First run per name is a silent baseline; a new Unique ID under a watched name on a later run is reported as UKSL\_DESIGNATED (match\_method=name).

## `signalGroups` (type: `array`):

Which change groups to evaluate: designation (DESIGNATED / DELISTED presence flips), variation (material regime / sanctions / name / designation-type changes). Default: both.

## `maxRunSeconds` (type: `integer`):

Wall-clock budget for CSV/XML download + watchlist diff. UKSL CSV is ~50 MB — allow enough headroom.

## `maxEvents` (type: `integer`):

Stop after this many unique change events are delivered (does not include RUN\_STATUS).

## `resumeFromCheckpoint` (type: `boolean`):

If true, resume entity snapshots and seen event\_uids from the default Key-Value Store.

## `sourceMode` (type: `string`):

fixture = local/CI / Apify Store daily auto-test default (no network; emits RUN\_STATUS only unless emitFixtureDemoEvents=true). live = official FCDO UKSL CSV/XML (no API key). Not HTML scrape; not ComplyAdvantage/OpenSanctions paid; not OFAC SDN.

## `emitFixtureDemoEvents` (type: `boolean`):

When sourceMode=fixture, if true push fabricated change events from local fixtures (unit/local demos only). Default false so Store daily auto-tests never emit fake UKSL events or charge uksl-designation-event-delivered. Production: leave false and use sourceMode=live. SAMPLE rows only: demo events use placeholder entities (never real companies/IDs), are flagged sample=true / isSample=true, and are NEVER charged.

## `asOfHint` (type: `string`):

Optional as\_of label. Live mode prefers Report Date from the FCDO file when present.

## `maxTotalChargeUsd` (type: `number`):

Optional soft budget hint for delivered change events. Platform ACTOR\_MAX\_TOTAL\_CHARGE\_USD also applies when set.

## `ukslCsvUrl` (type: `string`):

Optional override for the official FCDO UKSL CSV URL. Default: https://sanctionslist.fcdo.gov.uk/docs/UK-Sanctions-List.csv

## `ukslXmlUrl` (type: `string`):

Optional override for the official FCDO UKSL XML URL (used when preferFormat=xml).

## `preferFormat` (type: `string`):

csv (default) or xml. Both are official FCDO machine-readable files. CSV is the primary path in this build.

## Actor input object example

```json
{
  "uniqueIds": [
    "AFG0001",
    "AFG0003"
  ],
  "ofsiGroupIds": [],
  "names": [],
  "signalGroups": [
    "designation",
    "variation"
  ],
  "maxRunSeconds": 120,
  "maxEvents": 500,
  "resumeFromCheckpoint": true,
  "sourceMode": "fixture",
  "emitFixtureDemoEvents": false,
  "preferFormat": "csv"
}
```

# Actor output Schema

## `OUTPUT` (type: `string`):

JSON summary: delivered, charged, runStatus, checkpoint, stats, UKSL coverage limits

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch").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 '{}' |
apify call plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plym-actor-factory/uk-sanctions-list-counterparty-portfolio-watch"
        }
    }
}
```

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/00dpdmZlmdHBSOPAQ/builds/mf85R18tJC7oTDPbc/openapi.json
