Developer guide

UN sanctions API in Python: customer screening workers

Use the UN sanctions API in a Python worker for people and entities. Save screening IDs and preserve weak-alias labels for review.

250 free live screenings. One lifetime allowance. No credit card required.
On this page

Screen UN-listed identities from Python

Store one screening operation per customer in your Python worker queue. Submit the customer’s known name, entity type and identifying details; save the returned screening ID with the local customer reference.

Retrieve the retained result to construct the review record. Keep UN references and alias quality attached to each candidate instead of merging candidate identities or flattening them to names.

Prepare the synthetic exercise

Prepare an invented Python queue item and a local UN-style candidate fixture with a permanent reference and alternative birth dates. Test that the retained review record keeps those distinctions. A flattened name-and-date pair would lose useful information before a person ever reviews it.

Python setup and request files
RequirementValue or action
RuntimeUse Python 3.11 or later. The HTTP client uses urllib, json, and the standard library, so the screening example needs no pip package or provider SDK.
Client fileSave the client as screen.py.
Screening endpointPOST /api/v1/screenings
SANCTIONSKIT_API_KEYSandbox key; screenings:write and results:read scopes
SCREENING_OPERATION_IDPersist one operation ID; reuse only with the identical request body
request.jsonMira Calder: invented subject; sandbox@1: synthetic records
request.json: synthetic sandbox request
{
  "subject": {
    "name": "Mira Calder",
    "entityType": "person",
    "birthDate": "1984"
  },
  "package": "sandbox@1",
  "retention": "standard"
}

Implement the Python request boundary

The urllib worker performs source discovery before the production call and validates the selected coverage afterward. Save the screening reference before acknowledging the queue item. A worker restart should recover the same operation, not rebuild evidence from the newest public list.

Python: authenticated screening client
"""Python 3.11+. Run on your trusted backend; standard library only."""
import json, os, sys
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, build_opener, HTTPRedirectHandler

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl): return None

class ApiFailure(Exception): pass

def main():
    key, operation = os.environ['SANCTIONSKIT_API_KEY'], os.environ['SCREENING_OPERATION_ID']
    request = json.loads(Path(sys.argv[1] if len(sys.argv) > 1 else 'request.json').read_text(encoding='utf-8'))
    sandbox = request.get('package') == 'sandbox@1'
    source_ids = ['sandbox-synthetic'] if sandbox else request.get('sources')
    if not isinstance(source_ids, list) or not source_ids: raise ValueError('Select your sources.')
    opener = build_opener(NoRedirect())
    def api(path, body=None):
        headers = {'Authorization': 'Bearer ' + key}
        if body is not None:
            headers.update({'Content-Type': 'application/json', 'Idempotency-Key': operation})
        call = Request('https://www.sanctionskit.com/api/v1' + path, headers=headers,
                       data=None if body is None else json.dumps(body).encode(),
                       method='GET' if body is None else 'POST')
        try:
            response = opener.open(call, timeout=25)
        except HTTPError as error:
            response = error
        with response:
            envelope = json.load(response)
        if not 200 <= response.status < 300:
            code = envelope.get('error', {}).get('code', 'unknown')
            if not isinstance(code, str) or not 1 <= len(code) <= 80 or any(c not in 'abcdefghijklmnopqrstuvwxyz0123456789_' for c in code): code = 'unknown'
            raise ApiFailure('API HTTP ' + str(response.status) + ': ' + code)
        return envelope.get('data')
    if not sandbox:
        sources = api('/sources')
        if not isinstance(sources, list): raise ValueError('Invalid discovery response.')
        for source_id in source_ids:
            found = [source for source in sources if isinstance(source, dict) and source.get('id') == source_id]
            if len(found) != 1: raise ValueError('Required source missing or ambiguous.')
            source = found[0]
            if (source.get('availability') != 'available' or source.get('fresh') is not True or
                    not isinstance(source.get('capabilities'), list) or
                    request['subject']['entityType'] not in source['capabilities']):
                raise ValueError('Required source unavailable or incompatible.')
    result = api('/screenings', request)
    matches, coverage = result.get('matches'), result.get('coverage')
    if (not isinstance(result.get('id'), str) or not result['id'] or
            result.get('environment') != ('sandbox' if sandbox else 'production') or
            result.get('status') not in ['potential_match', 'no_match'] or
            not isinstance(matches, list) or (result['status'] == 'no_match') != (len(matches) == 0) or
            not isinstance(coverage, list) or len(coverage) != len(source_ids) or
            any(len([item for item in coverage if item.get('sourceId') == source_id and item.get('fresh') is True]) != 1 for source_id in source_ids)):
        raise ValueError('Incomplete screening result.')
    # Store result['id'] with your customer reference; retrieve evidence with results:read.
    print(json.dumps({'id': result['id'], 'status': result['status'], 'candidateCount': len(matches),
                      'sources': [item['sourceId'] for item in coverage]}))

try:
    main()
except ApiFailure as error:
    print(str(error), file=sys.stderr)
    sys.exit(1)
except Exception as error:
    print(str(error) if str(error) == 'Required source unavailable or incompatible.' else
          'Screening incomplete. Check credentials, request and source coverage.', file=sys.stderr)
    sys.exit(1)

Run the client

Run the sandbox request
python screen.py request.json

Confirm UN sanctions scope before production

Use un-consolidated for the Security Council’s consolidated individuals and entities source. Keep committee or regime context with the candidate and assess any separate national implementation needed by your process. This request does not select the separate designated-vessels source.

Production source-discovery contract
CheckRequired evidence
GET /api/v1/sourcesProduction key with sources:read; response data array
idun-consolidated
availability / freshavailable / true
capabilitiesContains the requested subject.entityType
currentVersionActive version; retrieved_at and last_checked_at are not publication dates
lastImportMay describe an import that is not the active version
Missing or ineligible sourceInspect disabledReason; required coverage stays incomplete
Displayed clientPerforms production discovery before screening
Read source discovery with your production key
curl --fail-with-body --max-time 25 --header "Authorization: Bearer $SANCTIONSKIT_API_KEY" "https://www.sanctionskit.com/api/v1/sources"

Select un-consolidated in the production request

Explicit change from the sandbox exercise
SettingProduction request
KeySeparate production credential with screenings:write
Coveragesources: ["un-consolidated"]; omit package
Operation IDNew ID for the changed body and environment
UsageProduction access and screening allowance apply
SubjectOnly lawfully held, known identifying facts; no invented date precision
request.json: explicit un-consolidated selection
{
  "subject": {
    "name": "Mira Calder",
    "entityType": "person",
    "birthDate": "1984"
  },
  "sources": [
    "un-consolidated"
  ],
  "retention": "standard"
}

Interpret UN sanctions evidence in this workflow

Store data.id and data.coverage with the permanent UN record reference. Retain alias quality and alternative or partial dates when constructing the review packet. When an analyst reopens the case, retrieve the saved result instead of replacing it with current source data.

UN sanctions candidate fields to retain
Fields in data.matches[]Review use
record.id and record.designations[].legalReferencesKeep the permanent UN reference with the candidate and use the published references to review the listing.
record.list and record.designations[].programsShow the committee or regime context; entries are not all subject to identical measures.
record.names[].quality and record.birthDatesRetain weak versus strong aliases and alternative or partial dates. A weak alias by itself does not establish identity.
  • data.id; data.coverage; data.versions; supplied disclaimer
  • data.matches[]: record, assessment, evidence, conflicts
  • GET /api/v1/results/{id}: results:read scope
  • Retained evidence and case review

Verify the application behavior

Recovery rules for the same saved operation
ConditionNext action
401 / 403Correct credentials or scopes before retrying
coverage_unavailable / stale_coverageKeep required coverage incomplete; no silent source substitution
Timeout after submissionRecover with the original Idempotency-Key and identical body
409Investigate an operation-key conflict
429 / 503Follow the specific error and Retry-After; an allowance cap may require a plan or usage change
Contract and safe diagnostic references
Operational reference
ReferenceUse
error.code / error.requestIdSafe recovery diagnostics; never log the submitted subject, key or full candidate response
potential_match / no_matchCompleted comparison outcomes; neither is an automatic business approval
  • Round-trip alternative birth-date values without choosing an invented single date.
  • Retain the permanent UN reference alongside the worker operation.
  • Reopen a retained result after a simulated source update without rewriting its evidence.

Sources and references

Frequently asked questions

Should a Python UN worker pick one birth date from several alternatives?

Preserve the available alternatives and their precision for review. Selecting one merely to simplify a local model can hide uncertainty or make the customer comparison appear more conclusive than the source supports.

Why keep the UN reference when the Python job already has a screening ID?

They identify different things. The screening ID identifies your completed comparison; the permanent UN reference identifies the source candidate. Retaining both lets reviewers trace the operation and the listing evidence.

What UN reference should my Python worker keep for later review?

Keep the permanent record reference and available committee or regime context with the screening ID. Also preserve the coverage and versions from that completed check. Current source data may change and cannot replace the history of the original result.

Can a Python UN review packet substitute today’s list for the saved result?

No. The packet should describe the evidence used by the completed operation. Current source data can support a separate new comparison, but it must not silently rewrite the earlier screening history.