Developer guide

Canadian sanctions API in Python: autonomous list customer screening

Use the Canadian sanctions API in Python for the autonomous list. Check customer records and preserve the cited regulation with each possible match.

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

Check Canadian customers from Python

The official Canadian consolidated resource describes its autonomous-sanctions scope and administrative status. Keep that source label in your Python application rather than treating it as a guarantee that every Canadian sanctions requirement was checked. Applicable regulations and other required controls need separate assessment.

When preparing a customer file, retain the legal name, known identifiers and source selection. Preserve accents and alternative spellings without replacing the original value. A normalized display label is useful for presentation but should not erase the identity facts submitted for comparison.

Prepare the synthetic exercise

Use invented customer names with accents in a Python input file and confirm that the JSON round trip preserves them. The sandbox tests encoding and operation handling. It does not confirm whether any real person appears on the Canadian autonomous sanctions list.

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 client discovers ca-autonomous before submitting a production subject. Keep the original input values beside any display normalization in your worker. Changing a label for search or presentation should not silently rewrite the identity facts in a saved operation.

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 Canadian sanctions scope before production

ca-autonomous concerns Canadian autonomous designations under SEMA and JVCFOA. This administrative consolidated list does not represent every measure under the United Nations Act. A worker result should name that scope instead of claiming a complete Canadian legal review.

Production source-discovery contract
CheckRequired evidence
GET /api/v1/sourcesProduction key with sources:read; response data array
idca-autonomous
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 ca-autonomous in the production request

Explicit change from the sandbox exercise
SettingProduction request
KeySeparate production credential with screenings:write
Coveragesources: ["ca-autonomous"]; 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 ca-autonomous selection
{
  "subject": {
    "name": "Mira Calder",
    "entityType": "person",
    "birthDate": "1984"
  },
  "sources": [
    "ca-autonomous"
  ],
  "retention": "standard"
}

Interpret Canadian sanctions evidence in this workflow

Retain data.coverage and the candidate’s available regulation references with data.id. Compare original names and identifiers with the customer record, then consult the cited measure for the intended activity. Missing evidence remains unknown rather than a reason to approve the customer.

Canadian sanctions candidate fields to retain
Fields in data.matches[]Review use
record.id and record.sourceIdKeep the autonomous-list record reference with the returned candidate.
record.designations[].legalReferences and wordingShow the available regulation and published designation context; consult the regulation when reviewing applicable restrictions.
record.names and identifiersKeep bilingual or alternative names and registration details as returned, with missing evidence visibly unknown.
  • 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 accented synthetic names without replacing the original spelling.
  • Keep the autonomous-list scope visible in the result summary.
  • Retain cited regulation evidence when a candidate enters review.

Sources and references

Frequently asked questions

Which Python packages are needed for Canadian autonomous screening?

The Python 3.11 example uses urllib and json from the standard library. Keep file decoding explicit and preserve the original subject strings when passing data from your customer preparation step to the worker.

Does ca-autonomous cover every Canadian sanctions measure?

No. It covers the autonomous designations described by the source and does not represent every measure under the United Nations Act. The cited regulations and any other required controls need separate consideration.

Should a Python customer worker treat the Canadian list as all Canadian sanctions law?

No. Label this check as Canadian autonomous sanctions and retain the relevant regulation with the candidate. Your review process must consider any other checks it requires. The worker should not turn the consolidated list result into a general legal approval.

What should a Canadian Python encoding test compare?

Compare the original invented name with the JSON value sent to the client and stored with the operation. A display normalization can be separate, but it should not silently replace the retained identity evidence.