Enrich or verify big lists with async jobs
POST /v1/jobs takes up to 100,000 rows, runs them in the background, and caps the whole job's spend.
POST /v1/bulk handles up to 1,000 rows while you wait. For bigger lists, start a job and come back for the results.
Start a job
curl https://api.wholayer.com/v1/jobs \
-H "Authorization: Bearer wl_test_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"operation":"enrich","max_credits":5000,"rows":[{"external_id":"crm-1","name":"Dana Okafor","company_domain":"fieldline.com","reveal":["work_email"]}]}'operation:enrich(each row is a/people/enrichbody) orverify(each row is{"email": "..."}).- Up to 100,000 rows. Add
external_idto any row to match results back to your CRM. - Files over 4 MB: gzip the body and send
Content-Encoding: gzip. - The MCP server has the same thing as
create_jobandget_job.
What it costs
Each row is billed exactly like a single call, and only on hits. Verify jobs charge each address once per job. Credit costs are on the pricing page.
max_credits (default 250, up to 2,000,000) caps the whole job. If the job could cost more than that in the worst case, it's refused before anything runs, and it never spends past it while running. Quote it first for free with POST /v1/estimate (operation bulk or verify, plus rows). Test keys (wl_test_...) run sandbox jobs and never spend.
Follow progress
GET /v1/jobs/{id}: status (queued, running, completed, failed), progress, and credits used.- Or pass
webhook_urlwhen you create it: we send a signedjob.completedorjob.failedevent, with the same signature headers as our other webhooks. - Up to 2 jobs can be queued or running per workspace at once.
A job stops early if your wallet, overage limit, or a key's daily cap runs out, or if the key that created it is revoked. Rows it didn't reach come back as not_processed; send them in a new job.
Get the results
GET /v1/jobs/{id}/results returns rows in order, 100 per page by default (limit up to 1,000, then cursor). Add ?format=csv to download everything as a spreadsheet.
People who opted out, are on a deletion list, or are on your suppression list are never returned: the row says person_not_available with a reason and isn't charged. We check again every time you read results, so someone who opts out after the job ran is withheld too. Results are deleted 30 days after the job was created.