---
title: "Enrich or verify big lists with async jobs"
description: "POST /v1/jobs takes up to 100,000 rows, runs them in the background, and caps the whole job's spend."
url: https://wholayer.com/help/async-bulk-jobs
---

# 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.

Category: Data API and MCP

`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
```bash
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/enrich` body) or `verify` (each row is `{"email": "..."}`).
- Up to 100,000 rows. Add `external_id` to 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_job` and `get_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](https://wholayer.com/pricing).

`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_url` when you create it: we send a signed `job.completed` or `job.failed` event, 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](https://wholayer.com/help/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.

## Related

- [Data API quickstart](https://wholayer.com/help/api-quickstart.md)
- [Keep an AI agent from overspending](https://wholayer.com/help/agent-spend-caps.md)
- [API error codes and what to do](https://wholayer.com/help/api-errors.md)
- [Upload your own suppression list](https://wholayer.com/help/your-suppression-list.md)
