Skip to main content

Best Practices

Recommendations for optimizing your sanctions screening workflow with SanctionsWise API.


Threshold Configuration​

Use CaseThresholdRationale
High-volume payments0.90-0.95Minimize false positives, fast throughput
Standard KYC0.85Balanced accuracy and review volume
Enhanced Due Diligence0.70-0.80Catch more potential matches
Investigation0.60-0.70Maximum coverage, manual review expected
Watchlist monitoring0.80Daily batch against customer base

Dynamic Threshold Strategy​

Consider adjusting thresholds based on context:

def get_threshold(transaction_amount, customer_risk_level):
"""Dynamic threshold based on risk context."""

# High-value transactions get lower threshold (more scrutiny)
if transaction_amount > 100000:
base_threshold = 0.70
elif transaction_amount > 10000:
base_threshold = 0.80
else:
base_threshold = 0.90

# Adjust for customer risk level
risk_adjustments = {
"low": 0.05, # Higher threshold = less scrutiny
"medium": 0.00,
"high": -0.10, # Lower threshold = more scrutiny
"pep": -0.15 # Politically exposed persons
}

return max(0.60, min(0.95, base_threshold + risk_adjustments.get(customer_risk_level, 0)))

Entity Type Optimization​

Always Specify Entity Type​

Providing entity_type improves matching accuracy by 5-10%:

{
"name": "Bank of Russia",
"entity_type": "organization"
}

Supported Types:

  • individual - Persons
  • organization - Companies, banks, government entities
  • vessel - Ships, boats
  • aircraft - Planes, helicopters
  • unknown - Let the engine auto-detect (slower)

Type Detection Helper​

def determine_entity_type(name, context):
"""Determine entity type from context clues."""

# Organization indicators
org_indicators = ["corp", "inc", "llc", "ltd", "bank", "ministry", "government"]
if any(ind in name.lower() for ind in org_indicators):
return "organization"

# Vessel indicators
vessel_indicators = ["vessel", "ship", "mv ", "ss "]
if any(ind in name.lower() for ind in vessel_indicators):
return "vessel"

# Default to individual
return "individual"

Identifier Matching​

Include Identifiers When Available​

Identifiers provide the highest-confidence matches (+15% boost):

{
"name": "John Smith",
"entity_type": "individual",
"identifiers": {
"passport": "AB1234567",
"date_of_birth": "1965-03-15",
"national_id": "123-45-6789"
}
}

Supported Identifier Types​

TypeAliasesExample
passportpassport_number, travel_documentAB1234567
national_idssn, id_number123-45-6789
tax_idtin, ein, vat_number98-7654321
registrationcompany_number, business_registration12345678
date_of_birthdob1965-03-15

Normalize Identifiers Before Sending​

import re

def normalize_identifier(id_value):
"""Remove formatting from identifiers."""
# Remove spaces, dashes, dots
return re.sub(r'[\s\-\.]', '', str(id_value).upper())

# Example: "AB-123-456" -> "AB123456"

Batch Screening Optimization​

When to Use Batch vs Single​

ScenarioRecommendedRationale
Less than 5 entitiesSingle requestsLower latency per entity
5-100 entitiesBatch requestAmortized setup cost
More than 100 entitiesMultiple batch requestsAPI limit is 100 per batch
Real-time paymentSingle requestImmediate response needed
Daily watchlist checkBatch requestThroughput over latency

Efficient Batch Processing​

def screen_large_list(entities, batch_size=100):
"""Screen large entity lists efficiently."""
results = []

for i in range(0, len(entities), batch_size):
batch = entities[i:i + batch_size]

response = requests.post(
f"{BASE_URL}/screen/batch",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={
"entities": batch,
"options": {"threshold": 0.85, "max_results_per_entity": 3}
}
)

batch_results = response.json()
results.extend(batch_results["results"])

# Log progress
print(f"Processed {min(i + batch_size, len(entities))}/{len(entities)}")

return results

Response Handling​

Status-Based Workflow​

def process_screening_result(result):
"""Process screening result based on status."""

screening_id = result["screening_id"]
status = result["status"]

if status == "clear":
# No matches - safe to proceed
log_decision(screening_id, "APPROVED", "No sanctions matches")
return {"action": "proceed", "review_required": False}

elif status == "match":
# High-confidence match - block immediately
match = result["matches"][0]
log_decision(screening_id, "BLOCKED",
f"High-confidence match: {match['matched_name']} ({match['confidence_score']:.0%})")
escalate_to_compliance(result)
return {"action": "block", "review_required": False}

elif status == "potential_match":
# Potential match - queue for review
log_decision(screening_id, "PENDING_REVIEW",
f"{result['match_count']} potential matches")
queue_for_manual_review(result)
return {"action": "hold", "review_required": True}

else:
# Unexpected status - treat as needing review
log_decision(screening_id, "ERROR", f"Unexpected status: {status}")
return {"action": "hold", "review_required": True}

Store Evidence Chain​

Always store the complete response for compliance:

def store_screening_record(result):
"""Store complete screening record for audit trail."""

record = {
"screening_id": result["screening_id"],
"timestamp": result["timestamp"],
"query": result["query"],
"status": result["status"],
"match_count": result["match_count"],
"matches": result["matches"],
"data_freshness": result["data_freshness"],
"processing_time_ms": result["processing_time_ms"]
}

# Store in your audit database
save_to_audit_log(record)

return record

Security Recommendations​

API Key Management​

  1. Never hardcode API keys - Use environment variables or secrets manager
  2. Rotate keys regularly - At least quarterly
  3. Use separate keys per environment - Dev, staging, production
  4. Monitor API key usage - Alert on unusual patterns
import os

# Load from environment
API_KEY = os.environ.get("SANCTIONSWISE_API_KEY")
if not API_KEY:
raise ValueError("SANCTIONSWISE_API_KEY environment variable not set")

Data Handling​

  1. Don't log full responses in production - Mask sensitive data
  2. Encrypt stored screening results at rest
  3. Implement audit logging for all API calls
  4. Set data retention policies per regulatory requirements

Testing Recommendations​

Test with Known Entities​

def test_sanctions_screening():
"""Test screening with known OFAC entries."""

# Test clear case
clear_result = screen_entity("John Smith", "individual", 0.85)
assert clear_result["status"] == "clear"

# Test match case
match_result = screen_entity("Vladimir Putin", "individual", 0.60)
assert match_result["status"] in ["match", "potential_match"]
assert match_result["match_count"] > 0

# Test organization
org_result = screen_entity("Bank of Russia", "organization", 0.60)
assert org_result["status"] in ["match", "potential_match"]


SanctionsWise API is a product of OrchestraPrime LLC