Skip to main content

Overview

This guide shows you how to validate individuals (persons) and organizations (companies) against sanctions lists and PEP databases using the KYC API. This is essential for compliance screening, onboarding customers, and monitoring business relationships.

Quick Example

import requests

url = "https://stg.kyc.legaltalent.ai/kyc"
headers = {
    "Authorization": "Bearer YOUR_TOKEN",
    "Content-Type": "application/json"
}

# Validate a person
person_payload = {
    "subject": {
        "full_name": "John Doe",
        "nationality": "US",
        "birth_date": "1985-03-15"
    },
    "list_name": "ofac"
}

response = requests.post(url, json=person_payload, headers=headers)
data = response.json()

if data.get("result", {}).get("is_match"):
    print("Match found in sanctions list!")
    print(f"Match count: {data['result']['match_count']}")
    print(f"Details: {data['result'].get('matches', [])}")
else:
    print("No matches found - entity is clear")

# Validate a company/organization
company_payload = {
    "subject": {
        "full_name": "Acme Corporation"
    },
    "lists": ["ofac", "un", "eu"]
}

response = requests.post(url, json=company_payload, headers=headers)
data = response.json()

Validating Individuals (Persons)

Basic Person Check

For basic screening, provide the person’s full name. Additional information improves accuracy:
# Basic check with name only
payload = {
    "subject": {
        "full_name": "Jane Smith"
    },
    "list_name": "ofac"
}

# Enhanced check with document and nationality
payload = {
    "subject": {
        "full_name": "Juan Pérez",
        "document_id": "12345678",
        "document_type": "CI",
        "nationality": "UY",
        "birth_date": "1990-05-20"
    },
    "list_name": "senaclaft_uy"
}

response = requests.post(url, json=payload, headers=headers)

Person Fields

FieldRequiredDescriptionExample
full_nameYes*Full name of the person”John Doe”
document_idNoGovernment-issued ID number”12345678”
document_typeNoType of document”Passport”, “CI”, “DNI”
nationalityNoTwo-letter country code (ISO 3166-1)“US”, “UY”
birth_dateNoDate of birth (YYYY-MM-DD)“1985-03-15”
* Required if no identifiers or document_id provided

Validating Organizations (Companies)

Organizations are validated using their legal name or tax identification numbers:
# Check company by name
payload = {
    "subject": {
        "full_name": "Global Trading Corporation"
    },
    "lists": ["ofac", "un", "eu"]
}

response = requests.post(url, json=payload, headers=headers)
data = response.json()

# Check company with tax ID
payload = {
    "subject": {
        "full_name": "Acme Corporation",
        "identifiers": [
            {
                "type": "tax_id",
                "value": "TAX-987654"
            }
        ]
    },
    "list_name": "ofac"
}

response = requests.post(url, json=payload, headers=headers)

Checking Multiple Lists

You can check against multiple lists simultaneously for comprehensive screening:
# Check against multiple lists
payload = {
    "subject": {
        "full_name": "John Doe",
        "nationality": "US",
        "birth_date": "1985-03-15"
    },
    "lists": ["ofac", "un", "eu", "senaclaft_uy"],
    "search_type": "composite"
}

response = requests.post(url, json=payload, headers=headers)
data = response.json()

# Process results
if data.get("result", {}).get("is_match"):
    matches = data["result"].get("matches", [])
    for match in matches:
        print(f"Found in {match.get('list_name')}: {match.get('match_details')}")

Available Lists

List NameDescriptionSupports
ofacOFAC Sanctions List (US Treasury)Persons, Organizations
unUN Security Council SanctionsPersons, Organizations
euEU Consolidated Financial SanctionsPersons, Organizations
senaclaft_uySENACLAFT Uruguay (PEP & Sanctions)Persons

Search Types

Choose the matching strategy based on your needs:
Search TypeDescriptionUse Case
exactExact name matchingHigh precision, strict matching
fuzzyApproximate string matchingHandle typos and variations
tokenWord-based matchingFind partial name matches
compositeCombined strategy (default)Balanced precision and recall
llm_enhancedLLM-filtered matchingMaximum precision with semantic filtering

Common Use Cases

1. Customer Onboarding

Validate new customers before account creation:
def validate_customer(name, nationality=None, document_id=None):
    payload = {
        "subject": {"full_name": name}
    }
    
    if document_id:
        payload["subject"]["document_id"] = document_id
    if nationality:
        payload["subject"]["nationality"] = nationality
    
    payload["lists"] = ["ofac", "un"]
    
    response = requests.post(url, json=payload, headers=headers)
    return response.json()

# Usage
result = validate_customer("John Doe", "US", "P123456")
if result.get("result", {}).get("is_match"):
    print("Customer flagged - review required")
else:
    print("Customer cleared for onboarding")

2. Batch Validation

Validate multiple entities at once:
entities = [
    {"full_name": "John Doe", "nationality": "US"},
    {"full_name": "Jane Smith", "nationality": "GB"},
    {"full_name": "Acme Corp"}
]

results = []
for entity in entities:
    payload = {
        "subject": entity,
        "lists": ["ofac", "un"]
    }
    response = requests.post(url, json=payload, headers=headers)
    results.append(response.json())

# Process results
for i, result in enumerate(results):
    entity_name = entities[i]["full_name"]
    if result.get("result", {}).get("is_match"):
        print(f"{entity_name}: FLAGGED")
    else:
        print(f"{entity_name}: CLEAR")

3. Continuous Monitoring with Watchlists

For ongoing monitoring, create a watchlist instead of one-time checks:
# Create a watchlist for continuous monitoring
watchlist_url = "https://stg.kyc.legaltalent.ai/kyc/watchlists"
watchlist_payload = {
    "name": "Customer Monitoring",
    "subjects": [
        {
            "full_name": "John Doe",
            "document_id": "12345678",
            "identifier_type": "document"
        },
        {
            "full_name": "Acme Corporation",
            "identifier": "TAX-987654",
            "identifier_type": "tax_id"
        }
    ],
    "lists_to_monitor": ["ofac", "un", "eu"],
    "check_frequency": "daily"
}

response = requests.post(watchlist_url, json=watchlist_payload, headers=headers)
print(response.json())

Error Handling

Always handle potential errors in your implementation:
def validate_entity(subject_data, lists=None):
    try:
        payload = {
            "subject": subject_data
        }
        if lists:
            payload["lists"] = lists
        else:
            payload["list_name"] = "ofac"
        
        response = requests.post(url, json=payload, headers=headers, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        
        if "error" in data:
            print(f"API Error: {data['error']}")
            return None
        
        return data
        
    except requests.exceptions.Timeout:
        print("Request timed out")
        return None
    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        return None
    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

Best Practices

  1. Provide Complete Information: Include nationality, birth_date, and document_id when available to improve matching accuracy.
  2. Use Multiple Lists: Check against multiple sanctions lists (ofac, un, eu) for comprehensive screening.
  3. Choose Appropriate Search Type: Use composite for balanced results, exact for strict matching, or llm_enhanced for maximum precision.
  4. Handle Errors Gracefully: Always implement error handling and timeouts in your integration.
  5. Consider Watchlists: For entities that need ongoing monitoring, use watchlists instead of repeated API calls.
  6. Respect Rate Limits: The API has rate limits (1000 requests per 5 minutes per IP). Implement retry logic with exponential backoff if needed.