# Stop an open job accepting items

> Sets `open` to false so the job completes once its queued work drains; takes no body.

Source: https://docs.spicrawl.com/api-reference/batch/batch-close

## POST /v1/batch/{batchID}/close

Operation ID: `batchClose`. API key scope: `batch`.

Sets `open` to false so the job completes once its queued work drains; takes no body. Idempotent: closing a
closed (or never-open) job succeeds and returns it unchanged. Next: poll GET /v1/batch/{batchID} until terminal.

### Example

```bash
curl -X POST "https://api.spicrawl.com/v1/batch/{batchID}/close" \
  -H "Authorization: Bearer $SPICRAWL_API_KEY"
```

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `batchID` | path | string | yes | The job `id` (26-character ULID as returned). The 32/36-character UUID form of the same id is also accepted. A malformed id and another tenant's id both answer 404. |

### Responses

#### 200

The job, now with `open` absent (false).

Headers: `X-Request-Id`.

`application/json`.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | Job id (26-character ULID). Use it in every /v1/batch/{batchID} path. |
| `project_id` | string | yes | Project the submitting API key belongs to. |
| `name` | string | no | The `name` you submitted. Absent when none. |
| `status` | string: `queued`, `running`, `paused`, `cancelling`, `completed`, `failed`, `cancelled` | yes | 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 | no | Present and true while the job accepts appended items; it will not complete until you close it. Absent means closed. |
| `status_url` | string | yes | Relative path to poll: /v1/batch/{id}. |
| `results_url` | string | yes | Relative path of the JSONL results: /v1/batch/{id}/results. |
| `params` | object | yes | The job-level scrape parameters as stored (your top-level BatchScrapeParams, without `url`), plus `credit_budget_micro` — the run ceiling the worker enforces. Always present, possibly only `credit_budget_micro`. |
| `total_items` | integer (int64) | yes | Number of items in the job. Fixed at submission unless items are appended to an open job. |
| `concurrency` | integer | yes | Effective per-job concurrency after clamping. |
| `priority` | integer | yes | Effective priority. |
| `max_attempts` | integer | yes | Effective attempts per item. |
| `failure_threshold` | integer (int64) | no | Failed-item count that aborts the job. Absent when not set. |
| `estimated_credits` | integer | yes | 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 (int64) | yes | Same as `estimated_credits` in micro-credits. |
| `progress` | object (`BatchJobProgress`) | yes | Live counters for the job, read from sharded counters. `completed` = succeeded + failed + cancelled + skipped; `remaining` = total − completed. |
| `submitted_at` | string (date-time) | yes | When the job was accepted. |
| `started_at` | string (date-time) | no | When the first item started. Absent until then. |
| `finished_at` | string (date-time) | no | When the job reached a terminal status. Cleared again by batchRetry. |
| `cancel_requested_at` | string (date-time) | no | When a cancel was accepted. The job may still be `cancelling` after this while in-flight items drain. |
| `results_expire_at` | string (date-time) | no | Results deadline: submitted_at + 72 h by default. After it, GET .../results answers 410. Download before this time. |
| `error_code` | string | no | Machine code of a job-level failure (for example the job was aborted). Absent otherwise. |
| `error_message` | string | no | Human-readable job-level failure reason. |
| `warnings` | array of string | no | 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. |

#### 401

Missing, invalid, revoked or expired key. Not retryable with the same key.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 403

The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 404

No such resource for this key's organization.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 500

Internal error. Retryable when `retryable` is true.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

Full OpenAPI spec: https://docs.spicrawl.com/openapi.yaml
