# Queue a bulk verification run

`POST /api/v1/email/bulk`

- Authentication: Secret API key as a bearer token
- Billing: Each row bills like a single lookup as it is processed; repeats inside the run and 7-day repeats are free.
- Group: Bulk

Queues up to 100,000 addresses and processes them in the background in chunks, with every chunk's mailbox probes sent concurrently. Creation answers `202` with the job's id and status. Poll `GET /api/v1/email/bulk/{jobId}` for progress, then download the result CSV from `GET /api/v1/email/bulk/{jobId}/results`. Jobs and their files are kept for 30 days.

Rows repeating an address already in the list are verified once and counted in `duplicate_count`. Addresses at a domain already known to be catch-all, addresses on your suppression list, and addresses your account reported delivered recently answer without a probe. A run that exhausts the balance stops with `stopped_reason: "insufficient_credits"` and keeps everything verified so far.

### Completion webhook

With a `webhook_url`, the finish (`completed`, `failed`, or `cancelled`) is POSTed there as `{ "event": "bulk_email_job.finished", "job": { "id", "status", "total", "processed", "deliverable", "risky", "undeliverable", "credits_used", "stopped_reason", "finished_at" } }`. The `X-Spaw-Signature` header is the HMAC-SHA256 of the exact raw body keyed with `webhook_secret`, which this response returns once and never again. Connection errors and 5xx answers are retried three times with a short backoff.

```php
$expected = hash_hmac('sha256', $request->getContent(), $secret);
abort_unless(hash_equals($expected, $request->header('X-Spaw-Signature')), 401);
```

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `emails` | string[] | yes | 1 to 100,000 addresses; each item at most 254 characters. |
| `webhook_url` | string | null | no | An https URL to POST when the job finishes. The response includes the signing secret — shown once, never again. At most 2,048 characters. |

## Example request

```bash
curl -X POST https://spaw.co/api/v1/email/bulk \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
  "emails": [
    "mia@acme.com",
    "info@example.org"
  ],
  "webhook_url": "https://www.example.com/hooks/spaw"
}'
```

## Responses

### 202 — The job was queued.

```json
{
    "success": true,
    "data": {
        "job": {
            "id": 512,
            "status": "queued",
            "total": 2,
            "duplicate_count": 0,
            "processed": 0,
            "deliverable": 0,
            "risky": 0,
            "undeliverable": 0,
            "credits_used": 0,
            "stopped_reason": null,
            "cancel_requested": false,
            "webhook_status": null,
            "created_at": "2026-09-03T10:12:44+00:00",
            "finished_at": null,
            "webhook_secret": "8fJ2…40 characters…Qk1"
        }
    }
}
```

### 401 — The key is missing, malformed, or revoked.

```json
{
    "success": false,
    "error": {
        "code": "UNAUTHENTICATED",
        "message": "Provide a valid API key as a bearer token.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 422 — The request body could not be validated; `error.errors` lists the fields.

```json
{
    "success": false,
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "The email field is required.",
        "errors": {
            "email": [
                "The email field is required."
            ]
        },
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

### 429 — Over 5 requests per second for the key. Retry after the limit resets.

```json
{
    "success": false,
    "error": {
        "code": "RATE_LIMITED",
        "message": "Too many requests. Retry after the limit resets.",
        "request_id": "req_01m1kgdm4xngzmbmff68g94w0c"
    }
}
```

## Error codes

- `UNAUTHENTICATED` — https://spaw.co/docs/errors/UNAUTHENTICATED
- `VALIDATION_FAILED` — https://spaw.co/docs/errors/VALIDATION_FAILED
- `RATE_LIMITED` — https://spaw.co/docs/errors/RATE_LIMITED

---

Canonical page: https://spaw.co/docs/api/create-bulk-job · OpenAPI document: https://spaw.co/openapi.json · All endpoints: https://spaw.co/docs/api
