Capture Batches
A capture batch captures every enabled page of one environment. Each page is a capture task.
Start a capture batch
POST /api/v1/projects/{project_id}/environments/{environment_id}/capture-batchesCaptures the environment's enabled pages now. No body. Minimum key role: Member.
curl -X POST https://deploydiff.com/api/v1/projects/{project_id}/environments/{environment_id}/capture-batches \
-H "Authorization: Bearer dd_…"Answers 201 with the new capture_batch, tasks included, and its address in the Location header. The batch is PENDING, or already RUNNING: read it again to follow it — see Capture after a deploy.
| Status | code | When |
|---|---|---|
| 404 | not_found | No such environment in the project. |
| 402 | usage_limit_reached | The organization's usage limit is reached for the period. |
| 402 | billing_blocked | The organization has an unpaid invoice that was cancelled. |
| 429 | capture_interval | The environment had a capture on demand too recently. retry_after gives the seconds to wait. |
| 422 | not_runnable | The batch was created and ended in Error at once: no enabled page, or the domain is not validated. capture_batch_id names it, and the message says why. |
The batch carries "trigger": "API", and counts in the environment's interval for captures on demand, together with those launched from the dashboard.
Read a capture batch
GET /api/v1/projects/{project_id}/capture-batches/{capture_batch_id}One capture_batch, with its tasks. Minimum key role: Participant.
List capture batches
GET /api/v1/projects/{project_id}/environments/{environment_id}/capture-batchesThe environment's capture batches, newest first, as a list of capture_batch objects without their tasks. Takes limit and cursor. Minimum key role: Participant.
?limit=1 reads the latest.
The capture_batch object
{
"object": "capture_batch",
"id": "00000000-0000-0000-0000-000000000002",
"environment_id": "00000000-0000-0000-0000-000000000001",
"environment_title": "Production",
"status": "DONE",
"trigger": "API",
"error": null,
"pages_over_plan_limit": null,
"auto_diffs": {
"settled_at": "2026-11-06T10:02:11.904Z",
"self": {
"status": "launched",
"diff_batch_id": "00000000-0000-0000-0000-000000000004"
},
"reference": {
"status": "launched",
"diff_batch_id": "00000000-0000-0000-0000-000000000011"
}
},
"task_counts": {
"pending": 0,
"running": 0,
"done": 12,
"error": 0
},
"tasks": [
{
"id": "00000000-0000-0000-0000-000000000005",
"url": "https://example.com/pricing",
"viewport": {
"width": 1280,
"height": 800
},
"page_title": "Pricing",
"status": "DONE",
"error_code": null
}
],
"created_at": "2026-11-06T10:00:00.31+00:00",
"updated_at": "2026-11-06T10:02:11.904+00:00"
}| Field | Type | Value |
|---|---|---|
environment_id | uuid string | The environment captured. |
environment_title | string | Its name today. |
status | string | PENDING, RUNNING, DONE, WARN or ERROR — see lifecycle. DONE, WARN and ERROR are final. |
trigger | string | What started it: MANUAL (from the dashboard), CRON (the environment's schedule), API (an API call). |
error | object | null | Why the batch failed as a whole, when it did: code and message (see below). null otherwise. A page that fails does not fail the batch: see tasks. |
pages_over_plan_limit | number | null | How many enabled pages the plan's limit per environment left out of this batch; null when none. |
auto_diffs | object | null | What happened to its automatic diffs. null until it is settled, shortly after the batch ends. |
task_counts | object | Its tasks by status: pending, running, done, error. |
tasks | array | One entry per page captured, by page title. Not in lists. |
tasks[].id | uuid string | The capture task's id. |
tasks[].url | string | null | The address captured. |
tasks[].viewport | object | null | width and height in pixels, as captured. |
tasks[].page_title | string | null | The page's title today; null once the page is deleted. |
tasks[].status | string | PENDING, RUNNING, DONE or ERROR. |
tasks[].error_code | string | null | Why the capture failed — see when a page capture fails. |
Batch errors
A batch in ERROR failed as a whole, before capturing any page: it has no tasks, and error says why. Test code in your code; show message to people as it is — it is written for them, and says what to do.
error.code | When | error.message |
|---|---|---|
NO_ENABLED_PAGES | The environment has no enabled page. | "This environment has no enabled pages to capture." |
DOMAIN_VALIDATION_FAILED | The domain could not be validated. | Names the domain and what was found there — HTTP 404, network error, no answer in time, a file too large or whose content is not the organization's id — or that it failed less than a minute ago and was not checked again; and the file to publish, as in the example below, unless the address is not a public domain name. |
DOMAIN_VALIDATION_CHECK_FAILED | We could not check the domain, on our side. | "The domain could not be checked because of an internal error, so no page was captured. Launch a new capture batch to try again." |
A batch can also end in ERROR with error at null, when it could not be started on our side: start it again.
{
"object": "capture_batch",
"id": "00000000-0000-0000-0000-000000000013",
"environment_id": "00000000-0000-0000-0000-000000000001",
"environment_title": "Production",
"status": "ERROR",
"trigger": "API",
"error": {
"code": "DOMAIN_VALIDATION_FAILED",
"message": "Domain https://example.com could not be validated (HTTP 404). Create /.well-known/deploy-diff-org-00000000-0000-0000-0000-000000000012.txt at your domain root containing exactly: 00000000-0000-0000-0000-000000000012"
},
"pages_over_plan_limit": null,
"auto_diffs": {
"settled_at": "2026-11-06T10:00:01.204Z",
"self": {
"status": "batch_failed"
},
"reference": {
"status": "batch_failed"
}
},
"task_counts": {
"pending": 0,
"running": 0,
"done": 0,
"error": 0
},
"tasks": [],
"created_at": "2026-11-06T10:00:00.31+00:00",
"updated_at": "2026-11-06T10:00:01.204+00:00"
}auto_diffs
Once the batch has ended and its automated diffs have been started, or not, auto_diffs says what happened to each: self, the comparison with the environment's previous capture, and reference, the comparison with its reference environment. settled_at is when it was decided.
Each of the two is an object with a status, and a diff_batch_id when a diff batch was started:
status | Meaning |
|---|---|
launched | A diff batch was started: diff_batch_id names it. It may end in Error, as when the reference environment has no capture yet. |
not_configured | This comparison is not set up on the environment. |
no_previous_capture | self on the environment's first capture: there is nothing earlier to compare with. |
usage_blocked | The organization's usage limit was reached: no diff was started. |
environment_deleted | The environment, its project or its organization was deleted. |
batch_failed | The capture batch ended in Error before capturing anything. |
failed | No diff could be started, on our side. |
swept | The batch was stopped by our safety net for stuck work, which starts no diff. |
A diff started automatically has "trigger": "AUTO". To wait for a capture and its diffs, see Capture after a deploy.