Developers
Ilzami API
Screen names against the UN Security Council Consolidated List and Kuwait's National List from your own systems: one name at a time, in batches, or continuously through ongoing monitoring. Every screening service on the Ilzami dashboard is available through the same API.
Overview
- Base URL:
https://ilzami.com. All paths below are relative to it. HTTPS only. - Format: JSON requests and responses, except file uploads (
multipart/form-data) and downloads (XLSX, CSV, PDF). - One record of truth: a scan made through the API is stored exactly like one made on the dashboard. It has a reference number, a public verification link and a PDF report, and it appears in your organization's history for your team to review.
- Times are ISO 8601 in UTC (
timestamp). Fields ending in_displaycarry the same time in Kuwait time, formatted for display.
Availability
API access is included in the Advanced - Sanctions Screening plan. Whether your current plan includes it is reported as package_limits.api_key_enabled by subscription status.
Each feature is also governed by your plan, through the API exactly as on the dashboard:
| Feature | Plan flag | Without it |
|---|---|---|
| Single-name screening, review, reports, history | Any active plan | — |
| Batch screening | features.batch_scanning | 403 with upgrade_required: true |
| Ongoing monitoring | features.ongoing_monitoring | 403 with upgrade_required: true |
Authentication
Getting your API key
- Sign in to the dashboard as your organization's admin and open Team management.
- Under API access, choose Generate API key.
- Copy the key straight away. It is shown once. If it is lost, regenerate it: the old key stops working at that moment.
The key belongs to your organization. Your organization has one key at a time. The key is valid while your subscription is active and your plan includes API access. It is renewed with your subscription, and the admin can revoke it at any time from the same page.
Sending the key
Send the key in the X-API-Key header on every request:
curl https://ilzami.com/api/subscription-status/ \
-H "X-API-Key: YOUR_API_KEY"
Whom a request acts as
A request made with the key has your organization admin's permissions, including recording match decisions, and counts against your organization's monthly search allowance. Scans and batches run with the key are credited to Organization API key, not to a person. That is how they appear in your history, reports, exports and verification pages.
By design, the key cannot manage your team, change or pay for your subscription, or delete accounts. Those actions stay on the signed-in dashboard.
Conventions and limits
| Rule | Value |
|---|---|
| Similarity threshold | Fixed at 75% for every customer. A threshold sent in a request is ignored. |
| Lists searched | Always both: UN Security Council Consolidated List (un) and Kuwait's National List (kuwait). A lists value sent in a request is ignored. |
| Record types | Individuals and entities are both screened. In a batch, a per-row type narrows that row. |
| Monthly allowance | Each screened name counts as one search: a single search, or each name in a batch. Shared by every member of the organization and the API key. It resets on the 1st of each month. |
| Batch size | Up to 1,000 unique names per request. Files up to 10 MB (.csv or .xlsx). |
| Pagination | page (from 1) and page_size (1–100, default 10) where noted. |
A name that cannot be screened is an error, never a clear result
If a name cannot be screened as entered (for example it is empty after cleaning, or contains no letters), the scan is still recorded with a reference number, but its status is "error" and error gives the reason. Never treat such a scan as "no match". A single-word name is screened against exact single-name listings only, and notice says so.
Errors
Errors from an endpoint carry "success": false and a human-readable message (some endpoints use error). Errors raised before the endpoint runs (a bad key, an inactive subscription, a plan without API access, a used-up allowance) carry detail instead. Download endpoints answer their own errors as plain text.
| Status | Meaning |
|---|---|
400 | The request is invalid, or the name could not be screened (see above). |
401 | The key is missing, invalid or expired, or the organization is inactive. The response's detail says which. |
403 | The subscription is not active, the plan does not include API access or the feature (upgrade_required: true), the monthly allowance is used up, or the record belongs to another organization. |
404 | The reference number or code was not found in your organization. |
Services covered
Every screening service on the Ilzami dashboard is available through the API. Civil ID card capture is not: it reads a name from a photographed card, and an integration already holds the name it screens.
| Dashboard service | API |
|---|---|
| Single-name screening | POST /api/search/ |
| Reviewing matches and recording decisions | GET /api/scan/{ref}/review/, POST /api/scan/{ref}/decisions/ |
| PDF and CSV scan reports, verification links | POST /api/scan/download/, /verify/{ref}/ |
| Search history and monthly Excel export | GET /api/search-history/, GET /api/search-history/export/ |
| Batch screening (template, upload, results, history) | /api/batch/… |
| Ongoing monitoring (watchlist, rescans, reports) | /api/monitoring/… |
| Plan, usage and features | GET /api/subscription-status/ |
| Civil ID card capture, team, billing, account deletion | Dashboard and app only |
Account and plan
GET /api/subscription-status/
Your plan, its limits and features, this month's usage, and the fixed screening scope. Call it at start-up to decide what your integration can offer.
{
"success": true,
"data": {
"member": {"email": "admin@example.com", "is_admin": true, "organization_name": "Example Co.",
"authorities": {"single_search": true, "batch_upload": true, "update_monitoring": true,
"notification_emails": true, "make_decisions": true}},
"subscription": {"package_name": "Advanced - Sanctions Screening", "period": "yearly",
"status": "active", "start_date": "2026-01-01T09:00:00+00:00",
"end_date": "2027-01-01T09:00:00+00:00", "is_active": true},
"package_limits": {"monthly_limit": 3000, "web_users_limit": 5, "api_key_enabled": true},
"features": {"batch_scanning": true, "id_capture": false,
"ongoing_monitoring": true, "monitoring_names_limit": 500},
"screening": {"threshold": 75, "lists": ["un", "kuwait"]},
"usage": {"current_month_searches": 412, "remaining_searches": 2588,
"total_searches": 9120, "team_members": 4}
}
}
Name screening
POST /api/search/ counts 1 search
Screen one name against both lists. The scan is stored and gets a reference number.
| Field | Type | |
|---|---|---|
search_term | string, required | The full name, in Latin or Arabic script. name is accepted as an alias. |
curl -X POST https://ilzami.com/api/search/ \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"search_term": "John Michael Smith"}'
{
"success": true,
"data": {
"reference_number": "5f0c2a8e-3b1d-4c7a-9a55-0e8f1d2b7c41",
"search_term": "John Michael Smith",
"threshold": 75,
"sanctions_lists_scanned": ["UN Security Council Consolidated List", "Kuwait's National List"],
"matches": [ { "name": "…", "similarity": 88, "list name": "…", "…": "…" } ],
"total_matches": 1,
"status": "completed", // or "error": the name was NOT screened
"error": null,
"notice": null, // set for single-word names
"single_name": false,
"timestamp": "2026-10-11T08:15:02.114+00:00",
"timestamp_display": "11 Oct 2026, 11:15 (Kuwait time)",
"conducted_by": "Organization API key",
"review": { "status": "pending", "total": 1, "decided": 0, "matches": [ … ] }
},
"subscription_info": {"package_name": "…", "monthly_limit": 3000, "searches_used": 413,
"remaining_searches": 2587, "subscription_end_date": "…"}
}
When status is "error", the response is 400 with message explaining why the name was not screened. The scan is still recorded and counted.
Match review and decisions
Every match is reviewed by recording a decision: false positive (a different person) or true match (the same person). A scan's review status is no_match, pending, cleared (every match a false positive), true_match or error.
GET /api/scan/{reference_number}/review/
One stored scan with its current review state, including decisions made by colleagues. Works for single scans and for each name of a batch.
Each item of data.review.matches has key (use it to record a decision, for example "un:KPi.033"), name, record_type, similarity, fields (the list record's details), differs_options (what can be marked as different for this record) and decision (the decision in force, or null).
POST /api/scan/{reference_number}/decisions/
Record a decision on one match. Decisions are append-only: to change one, post again. The earlier record stays in the audit log. Returns 201 with the scan as it now stands. For a name in a batch, the response also includes data.batch, the batch's updated counts.
| Field | Type | |
|---|---|---|
match_key | string, required | The match's key from the review. |
decision | string, required | false_positive or true_match |
what_differs | array | Required for false_positive: one or more keys from that match's differs_options, from date_of_birth, place_of_birth, nationality, id_number, country, address, other. |
note | string, required | Your reasoning, up to 2,000 characters. |
curl -X POST https://ilzami.com/api/scan/5f0c2a8e-3b1d-4c7a-9a55-0e8f1d2b7c41/decisions/ \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"match_key": "un:KPi.033", "decision": "false_positive",
"what_differs": ["date_of_birth", "nationality"],
"note": "Customer born 1990, Kuwaiti national; listed person born 1961."}'
Reports and verification
POST /api/scan/download/
The scan report as a file, built from the stored scan, with the decisions recorded on it.
| Field | Type | |
|---|---|---|
reference_number | string, required | The scan's reference number. |
format | string | pdf (default) or csv |
curl -X POST https://ilzami.com/api/scan/download/ \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"reference_number": "5f0c2a8e-3b1d-4c7a-9a55-0e8f1d2b7c41", "format": "pdf"}' \
-o report.pdf
GET /verify/{reference_number}/ public
The public verification page for a scan, the page the report's QR code opens. Share it with auditors. It needs no key. /verify/{reference_number}/pdf/ returns the same scan as a PDF.
Search history
GET /api/search-history/
Stored scans, newest first, paginated. Names from a batch that had no match are left out, since the batch keeps them.
| Query | |
|---|---|
scope | organization for everyone's scans. The default, member, returns the admin's own scans, which includes every API scan. |
days | Only the last N days. |
status | pending, true_match, cleared or no_match. |
exclude_batch | 1 to leave out batch scans entirely. |
include_matches | 1 to include each scan's stored matches. |
page, page_size | Pagination (page size 1–100, default 10). |
Response: data (rows with reference_number, search_term, status, total_matches, review, is_batch, batch_reference, member_email (who ran it, or Organization API key), via_api, timestamp, …), status_counts and pagination.
GET /api/search-history/export/?month=YYYY-MM
One calendar month of your organization's single-name scans as an Excel workbook, with review status, decisions and verification links. The default is the current month.
Batch screening plan feature
Screen up to 1,000 names in one synchronous request. Each name counts as one search. A batch larger than your remaining allowance is refused as a whole, before anything is screened. Every name gets its own reference number, verification link and PDF.
GET /api/batch/template/
The blank Excel template (name, type and optional date of birth per row). Results come back in the same workbook.
POST /api/batch/upload/ counts 1 search per name
Send names in any combination of these fields. Duplicate names are screened once.
| Field | Type | |
|---|---|---|
entries | array (JSON) | Objects with name (required), type (individual or entity) and dob. Best for system integrations. |
names | string | Names separated by new lines. |
file | file (multipart) | The filled-in template, .xlsx or .csv. |
curl -X POST https://ilzami.com/api/batch/upload/ \
-H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"entries": [
{"name": "John Michael Smith", "type": "individual", "dob": "1984-03-12"},
{"name": "Gulf Trading Company", "type": "entity"}
]}'
# or with a file
curl -X POST https://ilzami.com/api/batch/upload/ \
-H "X-API-Key: YOUR_API_KEY" -F "file=@customers.xlsx"
Response data: reference_code, total_names, names_with_matches, total_matches, names_with_errors, names_clear, names_limited, truncated (more than 1,000 names were sent), review (the batch's review summary) and names (each name with its status: match, clear or error, its matches and its own reference_number). Also subscription_info with your remaining allowance.
To record decisions on a batch match, use that name's reference_number with the decision endpoint.
GET /api/batch/history/
Your organization's 25 most recent batches, with status_counts. Filter with ?status=needs_review or ?status=reviewed. Add ?reference_code=… to get one batch's full results, in the same shape as the upload response.
POST /api/batch/download/
A batch's results as a file. Body: reference_code (required) and format: xlsx (default, the screening report with a PDF link per name) or csv.
Ongoing monitoring plan feature
Keep a watchlist of your customers. Ilzami rescreens the whole list whenever the UN or Kuwait list changes, and when you upload a new version. Each run is a monitoring scan with its own reference code. Members of your team with notification emails turned on are alerted when a run finds matches. Monitoring rescans do not count against the search allowance. The plan sets how many names the list may hold.
GET /api/monitoring/status/
names_limit, available_lists, list (the watchlist in force: version, name_count, uploaded_at, uploaded_by…, or null) and scans (the 50 most recent runs).
GET /api/monitoring/template/
The blank watchlist template, sized to your plan's name limit.
POST /api/monitoring/upload/
Upload (or replace) the watchlist as multipart/form-data with file (the template, .xlsx or .csv). The file is checked against the name limit before anything changes, so a rejected upload leaves the current list in force. The earlier version is kept. An initial scan starts right away. Its result appears under /api/monitoring/scans/ shortly after.
curl -X POST https://ilzami.com/api/monitoring/upload/ \
-H "X-API-Key: YOUR_API_KEY" -F "file=@watchlist.xlsx"
GET /api/monitoring/list/
The watchlist in force with every entry as Ilzami read it (data.list.entries).
GET /api/monitoring/list/download/
The watchlist in force as an Excel workbook.
GET /api/monitoring/scans/
The 50 most recent monitoring scans: reference_code, status, trigger_key (list_update when a sanctions list changed, list_upload, list_change when you replaced the watchlist, manual), triggered_by, total_names, names_with_matches, total_matches, names_with_errors, started_at, completed_at, alert_sent_at. Add ?reference_code=… for one scan with its full results.
POST /api/monitoring/download/
One monitoring scan as the Excel monitoring report. Body: reference_code.
Public endpoints
GET /api/packages/ public
The plans on offer, with prices, limits and included features. No key needed.
Full example
Onboarding check in Python: screen a customer, stop on an error, and keep the reference and PDF as evidence.
import os, requests
BASE = "https://ilzami.com"
session = requests.Session()
session.headers["X-API-Key"] = os.environ["ILZAMI_API_KEY"]
r = session.post(f"{BASE}/api/search/", json={"search_term": "John Michael Smith"}, timeout=30)
scan = r.json()["data"]
if scan["status"] == "error":
# The name was NOT screened. Never treat this as clear.
raise RuntimeError(scan["error"])
ref = scan["reference_number"]
print("Matches:", scan["total_matches"], "Review:", scan["review"]["status"])
print("Verify:", f"{BASE}/verify/{ref}/")
pdf = session.post(f"{BASE}/api/scan/download/", json={"reference_number": ref, "format": "pdf"})
open(f"ilzami-{ref}.pdf", "wb").write(pdf.content)
Support
Questions about the API, or need a feature your integration depends on? Write to support@techween.net.