---
title: "Get a batch job and its progress"
method: GET
path: "/v1/batch/{batchID}"
tags: ["Batch"]
---

# Get a batch job and its progress

`GET /v1/batch/{batchID}`

## Request

- Base URL: `https://api.spicrawl.com`
- URL: `https://api.spicrawl.com/v1/batch/{batchID}`
- Auth: HTTP bearer

Returns the job with live progress counters. Cheap to call: progress is read from sharded counters, not by
counting items. Poll until `status` is `completed`, `failed` or `cancelled`; a reasonable interval is 2-5 s for
jobs under ~1,000 items and 10-30 s for larger ones. If `open` is true the job will never finish on its
own — call `POST /v1/batch/{batchID}/close` when you are done appending.

## Response `200`

The job.

- BatchJob: The batch job, identical in shape across create, list, get, cancel and close. Poll `status_url` until `status` is terminal (`completed`, `failed`, `cancelled`), then read `results_url` before `results_expire_at`. There is no `credits_held` field: the hold is `estimated_credits`, the spend is `progress.credits_charged`.
  - `id` string, required: Job id (26-character ULID). Use it in every /v1/batch/{batchID} path.
  - `project_id` string, required: Project the submitting API key belongs to.
  - `name` string: The `name` you submitted. Absent when none.
  - `status` 'queued' | 'running' | 'paused' | 'cancelling' | 'completed' | 'failed' | 'cancelled', required: Job lifecycle. `queued` → `running` → `completed` | `failed`; cancel moves a live job to `cancelling`, then the worker moves it to `cancelled` once in-flight items drain. `paused` is an operator state (no public endpoint pauses). Terminal: `completed`, `failed`, `cancelled` — stop polling. A terminal job returns to `queued` after batchRetry.
  - `open` boolean: Present and true while the job accepts appended items; it will not complete until you close it. Absent means closed.
  - `status_url` string, required: Relative path to poll: /v1/batch/{id}.
  - `results_url` string, required: Relative path of the JSONL results: /v1/batch/{id}/results.
  - `params` object, required: The job-level scrape parameters as stored (your top-level BatchScrapeParams, without `url`), plus two keys the API sets and you cannot — `credit_budget_micro`, the run ceiling the worker enforces, and `default_proxy_tier` (`direct` or `datacenter`), the exit an item that sets no `proxy`, `premium_proxy`, `proxy_country` or `sticky_key` takes, decided at submission the way `/v1/scrape` decides it. Always present, possibly only those two.
  - `total_items` integer, required: Number of items in the job. Fixed at submission unless items are appended to an open job.
  - `concurrency` integer, required: Effective per-job concurrency after clamping.
  - `priority` integer, required: Effective priority.
  - `max_attempts` integer, required: Effective attempts per item.
  - `failure_threshold` integer: Failed-item count that aborts the job. Absent when not set.
  - `estimated_credits` integer, required: Credits RESERVED against the monthly allowance at submission — the dearest outcome of every item (whole credits, rounded down). It is a hold, not a charge; credits held for items that fail, are cancelled or skipped are returned to the monthly allowance within about a minute of the item finishing, by a background settler. Succeeded items keep their charge. Not increased by appends. Appended items are reserved when they are added, and the append's own total is its `X-Request-Cost`. Retrying failed items (`POST /v1/batch/{id}/retry`) holds their price again first and is refused with 402 if the allowance cannot cover it.
  - `estimated_credits_micro` integer, required: Same as `estimated_credits` in micro-credits.
  - `progress` BatchJobProgress, required: Live counters for the job, read from sharded counters. `completed` = succeeded + failed + cancelled + skipped; `remaining` = total − completed.
    - `total` integer, required: Items in the job (same as `total_items`). Grows when items are appended to an open job.
    - `completed` integer, required: Items in any terminal status.
    - `succeeded` integer, required: Items that succeeded. Only these are charged.
    - `failed` integer, required: Items that failed after all attempts. Re-run them with POST /v1/batch/{batchID}/retry.
    - `cancelled` integer, required: Items not run because the job was cancelled.
    - `skipped` integer, required: Items the worker skipped (for example after `failure_threshold` or `credit_budget` stopped the run).
    - `remaining` integer, required: Items not yet finished.
    - `percent_complete` number, required: completed / total × 100.
    - `credits_charged` integer, required: Credits actually charged so far, whole credits rounded down. Sum over succeeded items only. This is the real spend; compare with the job's `estimated_credits` (the hold).
    - `credits_charged_micro` integer, required: Same as `credits_charged` in micro-credits (1 credit = 1,000,000). Use this for exact accounting.
    - `bytes` integer, required: Total response bytes of finished items.
  - `submitted_at` string, date-time, required: When the job was accepted.
  - `started_at` string, date-time: When the first item started. Absent until then.
  - `finished_at` string, date-time: When the job reached a terminal status. Cleared again by batchRetry.
  - `cancel_requested_at` string, date-time: When a cancel was accepted. The job may still be `cancelling` after this while in-flight items drain.
  - `results_expire_at` string, date-time: Results deadline: submitted_at + 72 h by default. After it, GET .../results answers 410. Download before this time.
  - `error_code` string: Machine code of a job-level failure (for example the job was aborted). Absent otherwise.
  - `error_message` string: Human-readable job-level failure reason.
  - `warnings` string[]: Decisions made on your behalf at submission — a clamped `concurrency`, or items not yet handed to the queue (do NOT resubmit in that case). Present only on the create response.

## Other responses

- `401`: Missing, invalid, revoked or expired key. Not retryable with the same key.
- `403`: The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.
- `404`: No such resource for this key's organization.
- `500`: Internal error. Retryable when `retryable` is true.

---

[API](https://skmtc.dev/spicrawl/apis/spicrawl-api.md) · [All operations](https://skmtc.dev/spicrawl/apis/spicrawl-api/llms.txt) · [OpenAPI document](https://skmtc.dev/spicrawl/apis/spicrawl-api/revisions/c4b4c6f97219?raw)
