Real-Time AML Sanctions Screening in Python: A Complete Integration Guide
Learn how to integrate the SanctionShield AI API to screen customers, entities, and transactions against OFAC, UN, and EU sanctions lists in real time using Python.

Why Real-Time Sanctions Screening Is Non-Negotiable in 2026
The cost of a single sanctions violation has never been higher. OFAC's average civil monetary penalty reached $4.2 million in 2025, and enforcement actions have accelerated in 2026 with over 1,300 new designations in the first quarter alone. For financial institutions, fintechs, and any business handling cross-border payments, sanctions screening is a legal requirement — not a nice-to-have.
The challenge is speed. Batch screening runs nightly or weekly and misses the 28% of violations that occur between screening cycles when new designations are published. Real-time screening — triggered at customer onboarding, payment initiation, and periodic re-screening — closes this gap.
This guide shows you how to build a production-ready sanctions screening integration with the SanctionShield AI API in Python, covering:
- Customer onboarding screening
- Payment transaction screening
- Batch entity screening
- Handling fuzzy name matches
- Webhook integration for async workflows
Understanding Sanctions Lists
Before writing code, it helps to understand what you're screening against:
| List | Jurisdiction | Coverage |
|---|---|---|
| OFAC SDN | United States | ~12,000 individuals and entities |
| OFAC Non-SDN | United States | Consolidated, CAPTA, NS-MBS lists |
| UN Consolidated | International | ~800 entries across 12 committees |
| EU Consolidated | European Union | ~2,000 entries |
| UK HMT | United Kingdom | Post-Brexit independent list |
| Canada OSFI | Canada | FACFOA and SEMA lists |
SanctionShield AI screens against all major lists simultaneously in a single API call — no need to maintain and update separate list files.
Setup
pip install requests python-dotenv pydantic# .env
SANCTIONSHIELD_API_KEY=YOUR_API_KEYimport os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("SANCTIONSHIELD_API_KEY")
BASE_URL = "https://apivult.com/sanctionshield/v1"
HEADERS = {
"X-RapidAPI-Key": API_KEY,
"Content-Type": "application/json",
}Step 1: Screen an Individual at Onboarding
from pydantic import BaseModel
from typing import Optional
class IndividualScreenRequest(BaseModel):
full_name: str
date_of_birth: Optional[str] = None # ISO 8601: YYYY-MM-DD
nationality: Optional[str] = None # ISO 3166-1 alpha-2
id_number: Optional[str] = None
fuzzy_threshold: float = 0.85 # 0.0–1.0, higher = stricter matching
def screen_individual(person: IndividualScreenRequest) -> dict:
"""Screen an individual against all sanctions lists."""
response = requests.post(
f"{BASE_URL}/screen/individual",
headers=HEADERS,
json=person.dict(exclude_none=True),
timeout=10,
)
response.raise_for_status()
return response.json()
# Example: Screen a new customer at signup
result = screen_individual(IndividualScreenRequest(
full_name="John Smith",
date_of_birth="1985-04-12",
nationality="US",
fuzzy_threshold=0.88,
))
print(result)Sample response:
{
"screening_id": "scr_7x9k2m3p",
"status": "clear",
"match_count": 0,
"lists_checked": ["OFAC_SDN", "OFAC_NON_SDN", "UN_CONSOLIDATED", "EU_CONSOLIDATED", "UK_HMT"],
"screened_at": "2026-03-31T09:14:22Z",
"processing_ms": 87
}Step 2: Screen an Entity (Business)
class EntityScreenRequest(BaseModel):
entity_name: str
country: Optional[str] = None
registration_number: Optional[str] = None
aliases: Optional[list[str]] = None
fuzzy_threshold: float = 0.85
def screen_entity(entity: EntityScreenRequest) -> dict:
"""Screen a business entity against sanctions lists."""
response = requests.post(
f"{BASE_URL}/screen/entity",
headers=HEADERS,
json=entity.dict(exclude_none=True),
timeout=10,
)
response.raise_for_status()
return response.json()
# Example: Screen a counterparty before onboarding
result = screen_entity(EntityScreenRequest(
entity_name="Acme Trading LLC",
country="AE",
aliases=["Acme Trade", "ACME LLC"],
fuzzy_threshold=0.90, # stricter for entities
))Sample response with a fuzzy hit:
{
"screening_id": "scr_3p8n1q4r",
"status": "review_required",
"match_count": 1,
"matches": [
{
"list": "OFAC_SDN",
"matched_name": "ACME TRADING COMPANY",
"similarity_score": 0.91,
"entry_id": "SDN-17842",
"designation_date": "2024-11-03",
"program": "IRAN",
"additional_info": "Vessel operator linked to petroleum shipments"
}
],
"recommendation": "manual_review",
"screened_at": "2026-03-31T09:15:44Z"
}Step 3: Handle Match Statuses
from enum import Enum
class ScreeningStatus(str, Enum):
CLEAR = "clear"
REVIEW_REQUIRED = "review_required"
CONFIRMED_MATCH = "confirmed_match"
def process_screening_result(result: dict, entity_id: str) -> str:
"""
Apply business rules based on screening result.
Returns the action taken: 'approved', 'flagged', 'blocked'
"""
status = result.get("status")
if status == ScreeningStatus.CLEAR:
log_screening(entity_id, result, action="approved")
return "approved"
elif status == ScreeningStatus.REVIEW_REQUIRED:
# Queue for compliance officer review
create_review_task(entity_id, result)
log_screening(entity_id, result, action="flagged")
return "flagged"
elif status == ScreeningStatus.CONFIRMED_MATCH:
# Auto-block — no human override possible for confirmed hits
block_entity(entity_id, result)
notify_compliance_team(entity_id, result)
log_screening(entity_id, result, action="blocked")
return "blocked"
else:
raise ValueError(f"Unknown screening status: {status}")
def log_screening(entity_id: str, result: dict, action: str):
"""Persist screening record for audit trail."""
record = {
"entity_id": entity_id,
"screening_id": result["screening_id"],
"action": action,
"screened_at": result["screened_at"],
"lists_checked": result["lists_checked"],
"match_count": result["match_count"],
}
# Save to your database / audit log system
print(f"[AUDIT] {record}")Step 4: Screen a Payment Transaction
def screen_payment(payment: dict) -> dict:
"""
Screen all parties in a payment transaction simultaneously.
payment: { sender, recipient, amount, currency, reference }
"""
# Screen both sender and recipient in parallel
import concurrent.futures
parties = [
{"role": "sender", "data": payment["sender"]},
{"role": "recipient", "data": payment["recipient"]},
]
results = {}
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
futures = {
executor.submit(
screen_individual,
IndividualScreenRequest(**party["data"])
): party["role"]
for party in parties
}
for future in concurrent.futures.as_completed(futures):
role = futures[future]
results[role] = future.result()
# Payment is clear only if ALL parties are clear
all_clear = all(r["status"] == "clear" for r in results.values())
return {
"payment_id": payment.get("id"),
"overall_status": "clear" if all_clear else "blocked",
"party_results": results,
}
# Example usage
payment = {
"id": "txn_abc123",
"sender": {"full_name": "Alice Johnson", "nationality": "GB"},
"recipient": {"full_name": "Bob Martinez", "nationality": "MX"},
"amount": 15000,
"currency": "USD",
}
result = screen_payment(payment)
print(result)Step 5: Batch Screening for Periodic Re-Screening
Regulations require periodic re-screening of existing customers. With SanctionShield AI, you can batch-screen your entire customer base:
def batch_screen_customers(customer_list: list[dict]) -> list[dict]:
"""
Batch screen a list of customers.
Handles rate limiting with exponential backoff.
"""
import time
results = []
for i, customer in enumerate(customer_list):
try:
result = screen_individual(IndividualScreenRequest(**customer))
results.append({
"customer_id": customer.get("id"),
**result
})
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429: # rate limit
retry_after = int(e.response.headers.get("Retry-After", 1))
time.sleep(retry_after)
# retry once
result = screen_individual(IndividualScreenRequest(**customer))
results.append({"customer_id": customer.get("id"), **result})
else:
raise
# Progress logging
if (i + 1) % 100 == 0:
print(f"Screened {i + 1}/{len(customer_list)} customers")
return results
# Generate compliance report
def generate_screening_report(results: list[dict]) -> dict:
total = len(results)
clear = sum(1 for r in results if r["status"] == "clear")
review = sum(1 for r in results if r["status"] == "review_required")
blocked = sum(1 for r in results if r["status"] == "confirmed_match")
return {
"total_screened": total,
"clear": clear,
"review_required": review,
"confirmed_matches": blocked,
"clear_rate": f"{(clear / total * 100):.1f}%",
"generated_at": datetime.utcnow().isoformat() + "Z",
}Compliance Best Practices
1. Always Persist Screening Records
Every screening result must be stored for audit purposes. OFAC expects firms to maintain screening records for at least 5 years.
2. Document Your Fuzzy Threshold
Set your threshold in configuration, not in code, and document why it was chosen. A threshold of 0.85 is standard for individual screening; 0.90 is recommended for entity screening.
3. Re-Screen on List Updates
Subscribe to OFAC's SDN list update notifications (published at regulations.gov) and trigger a re-screen of high-risk customers whenever new designations are added.
4. Implement a Manual Review Workflow
review_required matches should never be auto-approved or auto-blocked. Build a compliance officer queue where trained staff review fuzzy matches before making a decision.
Integration Checklist
Before going live:
- Screening records persisted to audit log
- Manual review workflow for fuzzy matches
- Re-screening triggered on list updates
- Rate limiting and retry logic implemented
- Compliance officer alerts for confirmed matches
- Test with known SDN entries (OFAC publishes test cases)
Next Steps
Real-time sanctions screening is one layer of a broader AML compliance stack. Pair SanctionShield AI with FinAudit AI for invoice verification and GlobalShield for PII detection to build a comprehensive compliance pipeline.
Start with the SanctionShield AI API on APIVult and run your first screening in under 2 minutes.
More Articles
Top 5 Sanctions Screening APIs Compared (2026)
Compare the leading sanctions screening APIs for OFAC, UN, and EU compliance. See how SanctionShield AI stacks up on accuracy, speed, and pricing.
March 27, 2026
OFAC Sanctions Enforcement Hits Record Pace in 2026
1,300+ new sanctions designations, a $1.7M fine on a Florida school, and new BSA requirements for investment advisers. Here's what compliance teams need to know.
March 30, 2026