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.

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.
| Requirement | Value or action |
|---|---|
| Runtime | Use 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 file | Save the client as screen.py. |
| Screening endpoint | POST /api/v1/screenings |
| SANCTIONSKIT_API_KEY | Sandbox key; screenings:write and results:read scopes |
| SCREENING_OPERATION_ID | Persist one operation ID; reuse only with the identical request body |
| request.json | Mira Calder: invented subject; sandbox@1: synthetic records |
{
"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 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
python screen.py request.jsonConfirm 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.
| Check | Required evidence |
|---|---|
| GET /api/v1/sources | Production key with sources:read; response data array |
| id | ca-autonomous |
| availability / fresh | available / true |
| capabilities | Contains the requested subject.entityType |
| currentVersion | Active version; retrieved_at and last_checked_at are not publication dates |
| lastImport | May describe an import that is not the active version |
| Missing or ineligible source | Inspect disabledReason; required coverage stays incomplete |
| Displayed client | Performs production discovery before screening |
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
| Setting | Production request |
|---|---|
| Key | Separate production credential with screenings:write |
| Coverage | sources: ["ca-autonomous"]; omit package |
| Operation ID | New ID for the changed body and environment |
| Usage | Production access and screening allowance apply |
| Subject | Only lawfully held, known identifying facts; no invented date precision |
{
"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.
| Fields in data.matches[] | Review use |
|---|---|
| record.id and record.sourceId | Keep the autonomous-list record reference with the returned candidate. |
| record.designations[].legalReferences and wording | Show the available regulation and published designation context; consult the regulation when reviewing applicable restrictions. |
| record.names and identifiers | Keep 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
| Condition | Next action |
|---|---|
| 401 / 403 | Correct credentials or scopes before retrying |
| coverage_unavailable / stale_coverage | Keep required coverage incomplete; no silent source substitution |
| Timeout after submission | Recover with the original Idempotency-Key and identical body |
| 409 | Investigate an operation-key conflict |
| 429 / 503 | Follow the specific error and Retry-After; an allowance cap may require a plan or usage change |
Contract and safe diagnostic references
| Reference | Use |
|---|---|
| error.code / error.requestId | Safe recovery diagnostics; never log the submitted subject, key or full candidate response |
| potential_match / no_match | Completed 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
Authority and technical sources
SanctionsKit documentation
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.