Bulk verification
Queue up to 10,000 checks of one product in a single job. You get a job handle, never rows; results are read from the job or arrive by webhook.
Bulk jobs run many checks of one product in the background, four at a time. They are built for payout batches, portfolio re-verification and back-book remediation.
By design, a bulk request never returns rows inline. You receive a job handle, and per-row results are PII-free assertions you read from the job. That keeps 10,000 identifiers out of logs, chat transcripts and AI context windows.
Create a job
Body parameters
productstringrequiredOne of
nin,bvn,phone,business,account,signals,passport,drivers_license,voters_card. Face checks are not available in bulk.itemsarray of objectsrequired1 to 10,000 items. Each item is the request body you would send to that product endpoint.
namestringA label for the dashboard, up to 120 characters.
curl -X POST "http://localhost:4000/v1/bulk" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"product": "account",
"name": "Payout batch 42",
"items": [
{
"account_number": "0123456789",
"bank_code": "058",
"match": {
"name": "Clarke Griffin"
}
},
{
"account_number": "1234567890",
"bank_code": "044",
"match": {
"name": "John Doe"
}
}
]
}'{
"id": "job_1a2b3c4d5e6f7a8b",
"object": "bulk_job",
"product": "account",
"name": "Payout batch 42",
"status": "running",
"total": 2500,
"processed": 1180,
"counts": {
"verified": 1102,
"not_found": 61,
"failed": 17
},
"progress": 47,
"mode": "sandbox",
"created_at": "2026-10-19T09:41:07.512Z",
"completed_at": null
}Each item is validated and charged like a single request. A row that fails validation counts as failed without stopping the job. Every row gets its own idempotency key (<job id>:<row index>), so a row is never billed twice.
The whole job is checked against your entitlements when it is created: 403 not_entitled if your workspace cannot use the product, 400 too_many_items above 10,000 items.
Track progress
status moves from queued to running to completed. progress is a percentage of total, and counts splits processed rows into verified, not_found and failed.
curl "http://localhost:4000/v1/bulk/job_1a2b3c4d5e6f7a8b" \
-H "Authorization: Bearer sk_test_..."Instead of polling, subscribe to bulk.completed. It fires once, when every row has been processed, with the final job object:
{
"id": "evt_5c2e9a0b1d3f4e67",
"type": "bulk.completed",
"mode": "sandbox",
"created_at": "2026-10-19T09:52:40.118Z",
"data": {
"id": "job_1a2b3c4d5e6f7a8b",
"object": "bulk_job",
"product": "account",
"name": "Payout batch 42",
"status": "completed",
"total": 2500,
"processed": 2500,
"counts": {
"verified": 2391,
"not_found": 87,
"failed": 22
},
"progress": 100,
"mode": "sandbox",
"created_at": "2026-10-19T09:41:07.512Z",
"completed_at": "2026-10-19T09:52:40.118Z"
}
}One entry per processed row, in input order (row is 1-based). Entries are PII-free: a masked subject, status and match. To see a full record, retrieve the row's verification by reference_id; bulk rows are created through the bulk channel and never include data.
{
"object": "list",
"data": [
{
"row": 1,
"reference_id": "spm_0c1d2e3f4a5b6c7d8e9f0a1b",
"status": "verified",
"match": true,
"subject": "******6789"
},
{
"row": 2,
"reference_id": "spm_1d2e3f4a5b6c7d8e9f0a1b2c",
"status": "verified",
"match": false,
"subject": "******7890"
},
{
"row": 3,
"reference_id": "spm_2e3f4a5b6c7d8e9f0a1b2c3d",
"status": "not_found",
"match": false,
"subject": "******0000"
}
]
}List jobs
GET /v1/bulk lists your jobs, newest first, with limit (up to 100, default 50) and the before cursor. See Pagination.
CSV upload in the dashboard
Operations teams can run the same jobs without code: open Dashboard → Bulk, choose a product, and upload a CSV with one row per check and one column per request field. The job runs exactly like one created through the API, and its progress and per-row results appear in the dashboard.
From an AI assistant
The MCP server exposes bulk_verify and get_bulk_job. The assistant gets a job id, counts and a dashboard link, never the rows.
Questions? Email developers@speedmap.ng. This page is also available as Markdown.