Sanctions Screening API avatar

Sanctions Screening API

Pricing

from $4.25 / 1,000 delivered sanctions screening rows

Go to Apify Store
Sanctions Screening API

Sanctions Screening API

Screen up to 100 names against fresh official OFAC SDN primary and alias files plus the EU consolidated sanctions list. Get ranked potential-match evidence, exact source coverage, confidence, gaps, failure truth, billing metadata, and a manual review action. Not sanctions clearance or legal advice.

Pricing

from $4.25 / 1,000 delivered sanctions screening rows

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Share

Sanctions Screening — OFAC & EU List Checker

Check a name against the two sanctions lists that actually matter before you onboard a client, counterparty or vendor — the US Treasury OFAC SDN list (including its published aliases) and the EU consolidated financial sanctions list — read live from their own public exports. No API key, no login, no third-party screening vendor.

Official-List Name Screening Evidence: buyer input to evidence-backed action

Who uses it

Anyone onboarding clients, running KYC/KYB, or vetting a new counterparty needs a sanctions check. Commercial screening tools (Refinitiv World-Check, Dow Jones, ComplyAdvantage) run hundreds to thousands of dollars a month and are built for compliance teams, not a one-off check or a lightweight internal tool. The underlying data — OFAC's SDN list and the EU's consolidated list — is public and free; this Actor reads it directly.

This is a preliminary screening tool, not a compliance determination. Every row carries that disclaimer. Sanctions screening in any serious compliance workflow always ends with human review of the hits — this Actor is built to make that review list, not replace it.

What you get

  • Two official lists, screened together: OFAC SDN (US Treasury) and the EU consolidated financial sanctions list, both fetched fresh at the start of every run — never a stale cache.
  • OFAC's published aliases are screened too, not just primary names. OFAC's own guidance is explicit that checking primary names alone is an incomplete screen — a sanctioned party is routinely listed, and searched for, under an a.k.a./f.k.a./n.k.a. spelling. This Actor loads OFAC's own alias export alongside the primary list.
  • Honest partial-coverage flag. If one of the two source lists fails to load for a run, every row carries listsComplete: false so you know exactly what wasn't checked — rather than a clean-looking row that quietly skipped half the screen.
  • Ranked candidates, not one silent guess. matches returns every candidate above your score threshold (up to 25, ranked), because on a common name picking one match and hiding the rest is worse than showing you the list.
  • Tunable sensitivity. minScore defaults soft, on purpose — a missed hit is worse than a false one — and it's yours to tighten.
  • Runs on Apify: schedule it, monitor it, call it from the API or the MCP server, export to JSON/CSV/Excel, or push straight into your own pipeline.

Sources and rights

  • OFAC SDN list + aliases (US Treasury) — sanctionslistservice.ofac.treas.gov, the same SDN.CSV and ALT.CSV flat-file exports OFAC itself publishes for download.
  • EU consolidated list (financial sanctions) — webgate.ec.europa.eu, the EU's own public CSV export, names and aliases both.

Both are fetched fresh on every run — no scraping, no unofficial mirrors.

Matching

Names are normalized (uppercase, diacritics stripped, punctuation removed) and compared by token overlap: a match score is the fraction of the query's words found in a candidate entity's name (matched against its primary name or any of its aliases, whichever hits). A single distinctive word matched inside a longer name still counts as a strong hit — e.g. checking "Wagner" against an entry like "PRIVATE MILITARY COMPANY 'WAGNER'" scores 1.0. This is deliberate: a missed hit is worse than a false one, so the default threshold (minScore: 0.5) is soft, and it's tunable per your own risk tolerance.

Common/ambiguous names will produce more hits now that aliases are in scope, not fewer — that's expected of any sanctions screen and is why matches returns everything above the threshold (up to 25, ranked by score) for you to review, rather than silently picking one.

Official-List Name Screening Evidence: evidence-to-action workflow

How to run

  1. Open Try for free and review the live pricing panel for your Apify tier.
  2. Paste names into Names, one per row — people or companies.
  3. Press Start and read the results from the dataset — UI, API or webhook.

Pricing

Pay-per-event has two named units: the automatic apify-actor-start event and result-found for one delivered screening-result row. At the current FREE tier each is $0.005; BRONZE through DIAMOND apply the live volume discounts shown in the Apify pricing panel. Apify determines automatic start units from the run configuration, including allocated memory. The reviewed 512 MB default is expected to record one start unit; the real 2,048 MB legacy run below recorded two. Treat the pricing panel and each run ledger as authoritative.

With complete official-list coverage, a two-name FREE-tier run that delivers two useful rows is therefore $0.005 + 2 × $0.005 = $0.015. A normalized duplicate is not screened or result-billed again. An incomplete no-match, source-failure advisory, pricing diagnostic, or budget advisory does not intentionally emit result-found. Always treat the live pricing panel and the run's chargedEventCounts as the settlement authority; examples here explain the current contract rather than guaranteeing future prices.

If one of the two source lists fails to load for a run, every row that run is flagged listsComplete: false. In that state a match is still charged as usual, but a "no match" verdict is not charged — because that's the case where we ourselves are telling you not to treat the answer as clean. You only pay for an answer this Actor stands behind. If both lists fail to load entirely, every name in the run returns found: false with the reason, and none of it is charged.

Input contract

FieldTypeDescription
namesarray of stringsPerson or company names to screen against the OFAC SDN (+aliases) and EU consolidated sanctions lists, one per row. Up to 100.
minScorenumberMinimum score (0–1) for a candidate to be reported. Default 0.5. Lower = more (weaker) hits reported.
maxConcurrencyintegerParallelism (1–20, default 5).
{
"names": ["Yevgeniy Prigozhin", "Acme Trading LLC"]
}

Output contract

One dataset row per name. This is a real row from a real run, matches trimmed to the top few of 25:

{
"input": "Yevgeniy Prigozhin",
"found": true,
"match": true,
"matchCount": 25,
"matches": [
{ "listName": "OFAC SDN", "entityName": "PRIGOZHIN, Yevgeniy Viktorovich", "entityType": "individual", "program": "UKRAINE-EO13661, CYBER2, ELECTION-EO13848, RUSSIA-EO14024", "matchedName": "PRIGOZHIN, Yevgeniy Viktorovich", "matchedNameType": "primary", "score": 1 },
{ "listName": "EU Consolidated", "entityName": "Yevgeniy Viktorovich PRIGOZHIN", "entityType": "individual", "program": "UKR", "matchedName": "Yevgeniy Viktorovich PRIGOZHIN", "matchedNameType": "primary", "score": 1 },
{ "listName": "OFAC SDN", "entityName": "BALYTSKYI, Yevhen Vitaliiovych", "entityType": "individual", "program": "RUSSIA-EO14024", "matchedName": "BALITSKIY, Yevgeniy", "matchedNameType": "aka", "score": 0.5 },
"…"
],
"highestScore": 1,
"matchedName": "PRIGOZHIN, Yevgeniy Viktorovich",
"matchedNameType": "primary",
"listsChecked": ["OFAC SDN", "EU Consolidated"],
"listsComplete": true,
"disclaimer": "Screening signal only — NOT a legal/compliance determination; verify against official sources.",
"summary": "Yevgeniy Prigozhin — 25 potential matches (highest score 1). Top hit: PRIGOZHIN, Yevgeniy Viktorovich [OFAC SDN], program UKRAINE-EO13661, CYBER2, ELECTION-EO13848, RUSSIA-EO14024.",
"checkedAt": "2026-07-26T15:09:32.370Z"
}
FieldDescription
inputThe name you passed
foundWhether screening completed (false only if both lists failed to load)
matchWhether any candidate scored at or above minScore
matchCountHow many candidates cleared the threshold (capped at 25 in matches)
matchesRanked list of candidates: listName, entityName, entityType, program, score, plus matchedName/matchedNameType when the hit came through an alias rather than the entity's primary name
highestScoreScore of the top match (0 if none)
matchedName / matchedNameTypeThe top hit's own name and whether it was the entity's primary name or an alias (aka/fka/nka)
listsCheckedWhich source lists actually loaded and were screened this run
listsCompletefalse when one of the two lists failed to load — a "no match" row in that state is not a clean bill of health, and is not billed either
disclaimerAlways present — this is a signal, not a determination
summaryHuman-readable one-liner, notes an alias match by name when that's what fired

Related tools for adjacent workflows in compliance and public procurement.

ActorWhat it does
Sanctions Update Alert — Watchlist Change MonitorPair it in the compliance and public procurement workflow: Watch a list of names against OFAC SDN and EU consolidated sanctions lists and get ONLY the changes since...
Counterparty Risk Rollup — Sanctions, Courts, Registry, HiringPair it in the compliance and public procurement workflow: One call, one row per counterparty: sanctions screening (OFAC + EU), legal-entity registry (GLEIF),...
Litigation Check — Company Litigation History ScreenerPair it in the compliance and public procurement workflow: Screen a counterparty for litigation history across US federal PACER dockets, UK case law and Poland's...
Company Registry EnricherPair it in the compliance and public procurement workflow: Turn a company name, LEI or UK company number into an official registry card: legal name, status,...
Gov Tender RadarPair it in the compliance and public procurement workflow: Search EU public-sector tenders (TED Europa) by keyword or CPV code, optionally filtered by buyer country

FAQ

Does it need an API key or login? No — both source lists are public, keyless exports.

How fresh is the data? Both lists are downloaded fresh at the start of every run, straight from OFAC and the EU — not cached.

Is this legally sufficient for KYC/AML? No single automated tool is. This is a preliminary screening tool, not a compliance determination — use it to build your review list, not as your only control, and always verify a hit against the official OFAC/EU sources before acting on it.

Does it catch aliases (a.k.a. names)? Yes. OFAC's own alias export (a.k.a./f.k.a./n.k.a.) and the EU list's alias rows are both loaded and screened alongside primary names — a hit through an alias is marked as such via matchedNameType. Free-text alias mentions buried in OFAC's unstructured remarks field are not indexed separately.

What if a name is very common? Expect more hits now that aliases are in scope, not fewer — that's the nature of sanctions screening, not a bug. Review the ranked matches list; lower minScore catches more, higher minScore narrows to only near-exact hits.

What does listsComplete: false mean for what I'm charged? If a source list failed to load, a "no match" answer for that run is not charged — an unverifiable clean result is free. A match, or any result from a run where both lists loaded, is charged as usual.

Can I call it from an AI agent? Yes — it's a standard Apify Actor, callable via the Apify API or the Apify MCP server.

What this is NOT. Not a legal or compliance determination, and not a substitute for your own KYC/AML program — it is an inexpensive way to build the review list a human then checks against the official sources.

Found a wrong result, or need a source list we don't cover? Open an issue on this Actor's page.


Built by zinin. Questions? Telegram @timzinin.

What this Actor is — and is not

Sanctions Screening is an official-list name-screening evidence collector and review router. It downloads the supported OFAC and EU list files at run time, checks each unique submitted name against primary and alias names, returns ranked candidates, records exactly which list components were available, and tells an operator what to review next. The useful product is a repeatable, source-linked review queue—not an automated “clear” or “blocked” decision.

It is not an identity-verification service, beneficial-ownership graph, PEP database, adverse-media service, transaction-monitoring system, legal opinion, export-control classifier, KYC program or sanctions-clearance authority. A name can belong to many people or entities; a listed party can use another spelling; a program can have licenses or exceptions; and legal effect depends on facts and current law outside these files. Every decision row therefore keeps safeToAutomate: false.

Evidence and boundaries

The run requests OFAC's primary SDN file, alias file and address file plus the EU consolidated financial-sanctions list. Primary and alias names participate in matching. Bounded addresses are context only and do not increase the match score. The Actor does not use an unofficial mirror to hide an official-source outage.

Coverage is explicit:

  • listsChecked names list families that produced usable rows;
  • listsComplete is true only when the expected match-bearing components loaded;
  • sourceEvidence identifies the official list family and observation time;
  • partial is true when list coverage is incomplete;
  • dataGaps explains what the screen cannot establish;
  • a no-match on incomplete coverage is free and is never presented as clearance.

The EU public download token in the source URL is the list publisher's public access token, not a buyer credential. The Actor does not request a login, store a customer secret, submit a form, read a private page or enrich a name with personal data from another provider.

How to interpret a row

Start with found. When false, screening did not produce a usable observation and failureType, retryable and failureDiagnostics explain what happened. When true, inspect listsComplete before interpreting match.

match: true means at least one official-list name cleared the configured fuzzy threshold. It does not mean the submitted subject and listed party are the same person or legal entity. Review matchedName, matchedNameType, the ranked matches, programs and available addresses against first-party identity documents and the current official record.

match: false means no loaded name record cleared the threshold in this bounded run. It does not mean the subject is legally cleared, is not owned or controlled by a listed party, or cannot be affected by another program. A complete no-match can support a documented preliminary control; an incomplete no-match should be repeated after source recovery.

Field dictionary

The original screening fields remain backward compatible. Commercial115 adds a stable decision contract to every delivered row:

FieldBuyer meaning
recordTypescreening_result for a usable screen or advisory for failure/budget truth
schemaVersionVersion of the additive decision contract
entityIdStable normalized identity for the submitted name inside this Actor
observedAtWhen this run produced the observation
confidenceScore / confidenceBandConfidence in the evidence path, never confidence in legal clearance
confidenceReasonsWhy the available source path supports the assigned evidence score
confidenceRisksConditions that can make the evidence incomplete or ambiguous
sourceEvidenceOfficial list-family references retained for review
freshnessObservation-time freshness contract; consumers must recalculate age later
changeExplicitly unavailable because this is not a snapshot-diff product
dataGapsIdentity, ownership and legal facts not established by this row
negativeSignalsMachine-readable reasons for heightened review
recommendedActionBounded human next step, never an enforcement command
actionPriority / actionReasonReview urgency and the reason for it
interpretationBoundaryDurable statement of what the row cannot prove
safeToAutomateAlways false
failureType / retryable / partialFailure and coverage truth
billingSettlement-neutral delivery intent; current-run KVS OUTPUT proves paid, free, anomalous or unknown settlement

Confidence measures source availability and match-path clarity. A high-confidence potential match can still be the wrong person. Conversely, a lower-confidence result may simply reflect incomplete list coverage rather than greater or smaller legal risk.

Decision routing

  1. Normalize the intended subject using reliable customer or vendor records.
  2. Confirm listsComplete; repeat the screen if an expected source component was unavailable.
  3. Review the top candidate and every material alternative above your threshold.
  4. Compare date of birth, registration number, address, nationality, ownership and other identifiers in current primary records. This Actor does not perform that identity join.
  5. Read the current program entry, amendments, general licenses, specific licenses and applicable jurisdictional guidance.
  6. Escalate a plausible match to qualified compliance or legal reviewers.
  7. Record the human disposition, evidence and review date in your own case system.
  8. Re-screen according to your policy because lists and customer facts change.

Do not connect match directly to account closure, payment blocking, employment action or adverse customer treatment. This Actor deliberately returns a review instruction, not a final decision.

Example integration

Start a run with explicit input and an Apify bearer token:

curl -sS -X POST 'https://api.apify.com/v2/acts/zinin~sanctions-screening/runs?waitForFinish=180' \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"names":["Example Person","Example Trading LLC"],"minScore":0.7,"maxConcurrency":2}'

Read Dataset rows from the returned defaultDatasetId and the reconciled envelope from KVS key OUTPUT. The envelope reports requested, unique, duplicate, attempted, delivered, paid, free, withheld and source-failure counts. COMPLETE means no known source failure or withheld row; PARTIAL exposes one of those conditions; FAILED carries fatalError and may set replaySafe: false when linked delivery cannot be established.

For an Apify MCP client exposing call-actor, use:

{
"name": "call-actor",
"arguments": {
"actor": "zinin/sanctions-screening",
"input": {
"names": ["Example Person", "Example Trading LLC"],
"minScore": 0.7,
"maxConcurrency": 2
}
}
}

Billing and budget behavior

The run-start event is charged by Apify according to allocated memory. A result-found event is linked to a delivered usable screening row. Source failures, pricing diagnostics, incomplete no-match rows, normalized duplicates and budget-withheld advisories are not result-billed.

The billing critical section serializes “budget still available → deliver row with event → update receipt.” A worker cannot independently pass the spend check while another worker is charging. If the SDK throws during linked delivery or returns an ambiguous charged state, the Actor marks the run fatal and replaySafe: false instead of reporting success. KVS OUTPUT lets an operator compare Dataset delivery to settled event counts before downstream use.

Important limitations

  • Token-overlap matching is explainable but not phonetic, transliteration-complete or identity aware.
  • Short and common names can produce many false positives.
  • Misspellings, non-Latin scripts and aliases absent from official files can produce false negatives.
  • Addresses are bounded context and can be old, incomplete or shared.
  • The Actor does not evaluate 50-percent ownership rules or indirect control.
  • It does not cover every national, UN, export-control, law-enforcement or internal watchlist.
  • It does not establish whether a program applies to a specific transaction, product or jurisdiction.
  • Official publication timing, availability and corrections remain upstream dependencies.
  • minScore changes recall and precision; it does not change the legal standard.

Operational acceptance checklist

  • Verify the intended subject and permitted processing purpose.
  • Keep the exact input, threshold and observation time with the case.
  • Require complete expected source coverage for a clean preliminary screen.
  • Treat every potential match as an identity-review candidate, not a confirmed hit.
  • Treat every no-match as bounded evidence, not sanctions clearance.
  • Review current official records and applicable licenses.
  • Keep human approval before any adverse action.
  • Reconcile Dataset, OUTPUT and pay-per-event counts.
  • Do not retry a fatal run when replaySafe is false without inspecting delivery state.
  • Schedule re-screening according to your own risk policy.

Release evidence contract

Production publication is accepted only when the immutable source matches the reviewed local candidate, one bounded no-retry canary proves a potential or complete screen plus a free error, duplicate and budget-withheld outcome, Dataset rows reconcile with KVS OUTPUT, and settled pay-per-event counts cover measured Apify usage by the campaign floor. The public Store is then checked anonymously for title, Input, pricing, boundaries and exactly two 1600×900 selling visuals.

Real run-bound evidence

The projections below come from retained Apify run, Dataset and KVS records. They are included to show what the existing production path proved and what the rejected candidate did not prove. They are not fabricated examples and they do not substitute for the required future no-retry canary of the current source.

Successful production potential-match screen

Run HGGEq1xUfOEAWFsDL used production build xY7T3qFzAXtkJhwhU, completed SUCCEEDED, and wrote one row to Dataset Yo0Oy8qZHINQBsnE1. The submitted name was Rosneft. The row recorded three potential candidates across both official list families, with the top normalized score equal to 1. The platform ledger recorded result-found: 1. That production build predated the current KVS OUTPUT contract, so its default KVS Lhsm5niWPhPf1BQwt returned no OUTPUT record; the missing receipt is disclosed rather than reconstructed.

{
"evidenceAccepted": true,
"runId": "HGGEq1xUfOEAWFsDL",
"buildId": "xY7T3qFzAXtkJhwhU",
"status": "SUCCEEDED",
"memoryMbytes": 2048,
"datasetId": "Yo0Oy8qZHINQBsnE1",
"keyValueStoreId": "Lhsm5niWPhPf1BQwt",
"chargedEventCounts": {
"apify-actor-start": 2,
"result-found": 1
},
"usageTotalUsd": 0.0011474827882498504,
"datasetRow": {
"input": "Rosneft",
"found": true,
"match": true,
"matchCount": 3,
"highestScore": 1,
"matchedName": "ROSNEFT AERO",
"matchedNameType": "alias",
"listsChecked": ["OFAC SDN", "EU Consolidated"],
"listsComplete": true,
"summary": "Rosneft — 3 potential matches (highest score 1). Top hit: RN AERO [EU Consolidated] (matched on alias ROSNEFT AERO, alias), program UKR."
},
"outputHttpStatus": 404
}

This proves that the legacy production source could load both list families and deliver a billed potential-match row. It does not prove that ROSNEFT AERO and the submitted subject are the same legal entity, and the row must not be treated as a final sanctions determination. It also does not prove the enhanced run receipt, because that build did not write one.

Second accepted production screen and measured economics

Run BSCIRB2pkgD5PKIjE used the same production build xY7T3qFzAXtkJhwhU on 9 August 2026. It completed SUCCEEDED, wrote one row to Dataset ewxEs7bpsKK5NvcKT, and used KVS vpnLECjW2YQg2yfeX. Authenticated GET readback confirmed the exact input Rosneft, minScore:0.6, maxConcurrency:3, complete OFAC/EU coverage, three candidates, top score 1, and an alias top match ROSNEFT AERO. The event ledger recorded start2/result1. Platform runtime usage was $0.0008573099164267383; the retained gate calculated 16.622× gross charge-to-platform-runtime-usage coverage. That is measured execution economics, not profit, customer demand, or a legal conclusion.

{
"evidenceAccepted": true,
"runId": "BSCIRB2pkgD5PKIjE",
"buildId": "xY7T3qFzAXtkJhwhU",
"status": "SUCCEEDED",
"startedAt": "2026-08-09T05:19:58.226Z",
"finishedAt": "2026-08-09T05:20:05.384Z",
"datasetId": "ewxEs7bpsKK5NvcKT",
"keyValueStoreId": "vpnLECjW2YQg2yfeX",
"input": {
"names": ["Rosneft"],
"minScore": 0.6,
"maxConcurrency": 3
},
"chargedEventCounts": {
"apify-actor-start": 2,
"result-found": 1
},
"usageTotalUsd": 0.0008573099164267383,
"coverageMultiple": 16.622,
"dataset": {
"rows": 1,
"input": "Rosneft",
"found": true,
"match": true,
"matchCount": 3,
"highestScore": 1,
"matchedName": "ROSNEFT AERO",
"matchedNameType": "alias",
"listsChecked": ["OFAC SDN", "EU Consolidated"],
"listsComplete": true,
"checkedAt": "2026-08-09T05:20:04.676Z"
},
"outputHttpStatus": 404
}

This second accepted run independently proves the same legacy product unit and official-list coverage. The missing legacy OUTPUT is again stated explicitly; the new enhanced receipt still requires the one future exact-build canary.

Rejected 512 MB candidate and measured remediation

Run eRZReWfgZqb6Q3uTn used candidate build qhPazqB0wlur0doz9 with 512 MB. It loaded 39,382 OFAC names, then exited 137 before completing the EU list or screening. Dataset cxTutKVAtQXafPFH8 stayed empty, KVS tYYHj6Eyauzh2bsqZ returned no OUTPUT, and the event ledger recorded the start event but no result-found. There was no retry.

{
"evidenceAccepted": false,
"runId": "eRZReWfgZqb6Q3uTn",
"buildId": "qhPazqB0wlur0doz9",
"buildNumber": "0.2.1",
"status": "FAILED",
"exitCode": 137,
"memoryMbytes": 512,
"timeoutSecs": 180,
"maxTotalChargeUsd": 0.017,
"datasetId": "cxTutKVAtQXafPFH8",
"keyValueStoreId": "tYYHj6Eyauzh2bsqZ",
"datasetRows": 0,
"outputHttpStatus": 404,
"chargedEventCounts": {
"apify-actor-start": 1,
"result-found": 0
},
"usageTotalUsd": 0.0002619125844008393,
"noRetry": true
}

The current implementation addresses the measured cause rather than hiding it with a larger default. CSV parsing is lazy, official files are downloaded and processed sequentially, tokens are attached in place, and implausibly small official-list outputs fail coverage checks. A local profile on fresh official files measured these inputs: SDN 5,632,887 bytes and 19,199 named rows; ALT 1,063,098 bytes and 20,183 aliases; ADD 1,676,170 bytes; EU 25,166,172 bytes and 31,053 named rows. The profile process peaked at 175,620,096 RSS bytes, below the reviewed 512 MB default. This is strong local evidence for the memory fix, but only a future exact-build cloud canary can prove the hosted runtime and settlement contract.

Happy, partial, and failure output

Happy complete row

A complete screen has found:true, listsComplete:true, a bounded ranked matches array, evidence confidence and safeToAutomate:false. The Dataset row records billing intent only. KVS OUTPUT must bind the current run and Dataset, show resultEvent.before → after with exact delta 1 for each confirmed paid row, and reconcile requested, unique, duplicate, attempted, delivered, paid and free counts.

Partial official coverage

If an official component is unavailable or implausibly small, the run exposes it in source.listErrors, rows carry listsComplete:false, and partial:true. A potential match may remain useful review evidence; an incomplete no-match is delivered free because the missing source could contain the match. It is never described as clearance.

{
"found": true,
"match": false,
"listsChecked": ["OFAC SDN"],
"listsComplete": false,
"partial": true,
"recommendedAction": "REPEAT_AFTER_OFFICIAL_SOURCE_RECOVERY",
"billing": {
"billable": null,
"billingEligible": false,
"billingIntent": "free_dataset_write",
"eventName": null,
"settlementSource": "current_run_kvs_output"
}
}

Failed or ambiguous delivery

An unreadable pre-push named counter fails before Dataset delivery. A thrown linked push sets unknownDeliveryCount, fatalError, and replaySafe:false. A returned push followed by an unreadable named counter is a known Dataset delivery with unknownSettlementCount:1; a contradictory aggregate receipt produces anomalousChargeCount:1. All post-push uncertainty is terminal and must not be blindly retried. Reconcile the original run, Dataset, KVS and platform event ledger.

Integration recipes

JavaScript client

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zinin/sanctions-screening').call({
names: ['Example Person', 'Example Trading LLC'],
minScore: 0.7,
maxConcurrency: 2,
});
const [{ items }, outputRecord] = await Promise.all([
client.dataset(run.defaultDatasetId).listItems({ clean: true }),
client.keyValueStore(run.defaultKeyValueStoreId).getRecord('OUTPUT'),
]);
if (!outputRecord?.value) throw new Error('Missing current-run OUTPUT');
if (outputRecord.value.runId !== run.id) throw new Error('Receipt belongs to another run');
if (outputRecord.value.status === 'FAILED'
|| outputRecord.value.unknownDeliveryCount > 0
|| outputRecord.value.unknownSettlementCount > 0
|| outputRecord.value.anomalousChargeCount > 0) {
throw new Error('Manual reconciliation required before any rerun');
}
console.log({ rows: items.length, paid: outputRecord.value.paidRowCount });

Python client

from apify_client import ApifyClient
import os
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('zinin/sanctions-screening').call(run_input={
'names': ['Example Person', 'Example Trading LLC'],
'minScore': 0.7,
'maxConcurrency': 2,
})
rows = list(client.dataset(run['defaultDatasetId']).iterate_items())
output = client.key_value_store(run['defaultKeyValueStoreId']).get_record('OUTPUT')
if not output or output['value']['runId'] != run['id']:
raise RuntimeError('Missing or foreign current-run receipt')
if (output['value']['status'] == 'FAILED'
or output['value']['unknownDeliveryCount'] > 0
or output['value']['unknownSettlementCount'] > 0
or output['value']['anomalousChargeCount'] > 0):
raise RuntimeError('Inspect delivery settlement before rerun')
print({'rows': len(rows), 'paid': output['value']['paidRowCount']})

Webhook consumer

Use an Actor run terminal webhook only as a notification. Read the run object, then fetch OUTPUT from that run's defaultKeyValueStoreId. Reject a missing or foreign runId. Compare Dataset item count with deliveredRowCount, platform chargedEventCounts['result-found'] with paidRowCount, and inspect unknown/anomalous counters before moving the rows into a case-management queue.

Commercial playbooks

  • Onboarding: screen exact counterparty names, retain official-list evidence, and route every plausible match to identity and legal review before any account or payment action.
  • Periodic re-screening: schedule independent observations under the buyer's policy and compare retained rows downstream; this Actor does not silently invent a cross-run clearance state.
  • Case enrichment: attach the bounded row and source links to an existing private case, preserving access, correction, retention, and reviewer controls rather than publishing the result.

Operating guide

  • Keep names at 100 or fewer and use 1–200 characters per value.
  • Normalize and deduplicate subjects in your source system, but retain the buyer-entered string for case evidence.
  • Use a higher minScore only as part of a documented screening policy; do not tune it merely to eliminate inconvenient candidates.
  • Keep maxConcurrency modest when downstream Dataset or webhook consumers have rate constraints. Matching shares an in-memory read-only index, while paid delivery is serialized.
  • Archive the run ID, source observation time, threshold, official list families, matched name, program and human disposition.
  • Apply retention and access controls because names and case dispositions may be personal data.
  • Re-screen according to policy and official-list update cadence; this Actor is a point-in-time observation, not a durable clearance.
  • Never infer beneficial ownership, control, identity or legal effect from a fuzzy name alone.
  • Never retry an unknown or anomalous settlement without reading the original platform ledger.