---
title: "Re-run a job's failed items"
method: POST
path: "/v1/batch/{batchID}/retry"
tags: ["Batch"]
---

# Re-run a job's failed items

`POST /v1/batch/{batchID}/retry`

## Request

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

Resets every item with status `failed` back to queued and re-dispatches it; takes no body. Succeeded, cancelled
and skipped items are never re-run. If the job was terminal it goes back to `queued` and `finished_at` is
cleared; if it is still running, the reset items join the current run. Item `attempts` is NOT reset.
No items failed → a no-op returning the job unchanged with `items_reset: 0`. A failed item whose hold was already
returned to the monthly allowance holds its price again first; `402` if the allowance cannot cover it, and then
nothing is reset.
Next: poll GET /v1/batch/{batchID} again.

## Response `200`

Failed items reset. If `items_dispatched` < `items_reset`, the rest are picked up by the recovery sweep; do not call retry again for them.

- BatchRetryResponse
  - `job` BatchJob, required: 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.
  - `items_reset` integer, required: Failed items reset to queued. 0 means nothing had failed and nothing changed.
  - `items_dispatched` integer, required: Of those, how many reached the dispatch queue now. The rest are recovered by the worker's sweep.

## Other responses

- `401`: Missing, invalid, revoked or expired key. Not retryable with the same key.
- `402`: `ERR::LIMIT::QUOTA_EXCEEDED`: the organization is out of credits. Not retryable until credits are added.
- `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)
