# Batches (/api-v1/batches)



The Batch API is the non-interactive async lane. Upload a [file](/api-v1/files) of one chat-completion request per line, create a batch over it, and Zumik runs each request through the real provider path and writes the results back as an output file you download.

## Create a batch [#create-a-batch]

```
POST https://api.zumik.ai/v1/batches
```

### Parameters [#parameters]

<ParamField body="input_file_id" type="string">
  The id of an uploaded [file](/api-v1/files) holding the batched requests, one JSON request per line (JSONL). Required.
</ParamField>

<ParamField body="endpoint" type="string">
  The endpoint every request targets. Must be `/v1/chat/completions` (the only batch endpoint wired today).
</ParamField>

<ParamField body="completion_window" type="string" default="24h">
  The completion window. OpenAI accepts `"24h"`.
</ParamField>

<ParamField body="metadata" type="object">
  Arbitrary metadata to attach. Optional.
</ParamField>

### Input file format [#input-file-format]

Each line is one request with a `custom_id`, the `method`, the target `url`, and the chat-completions `body`:

```json
{"custom_id":"r1","method":"POST","url":"/v1/chat/completions","body":{"model":"code.fast","messages":[{"role":"user","content":"hi"}]}}
{"custom_id":"r2","method":"POST","url":"/v1/chat/completions","body":{"model":"auto.cheapest","messages":[{"role":"user","content":"yo"}]}}
```

### Request [#request]

<CodeGroup>
  ```bash title="curl"
  curl https://api.zumik.ai/v1/batches \
    -H "Authorization: Bearer zk_live_..." \
    -H "Content-Type: application/json" \
    -d '{"input_file_id":"file-9f2a...","endpoint":"/v1/chat/completions"}'
  ```

  ```python title="OpenAI SDK"
  from openai import OpenAI

  client = OpenAI(base_url="https://api.zumik.ai/v1", api_key="zk_live_...")

  batch = client.batches.create(
      input_file_id="file-9f2a...",
      endpoint="/v1/chat/completions",
      completion_window="24h",
  )
  print(batch.id, batch.status)
  ```
</CodeGroup>

### Response [#response]

```json
{
  "id": "batch_01jy7n3q8v6m4k2x...",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "input_file_id": "file-9f2a...",
  "completion_window": "24h",
  "status": "validating",
  "created_at": 1750000123,
  "metadata": {},
  "request_counts": { "total": 2, "completed": 0, "failed": 0 }
}
```

<ResponseField name="id" type="string">
  The batch id, `batch_...`.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"batch"`.
</ResponseField>

<ResponseField name="status" type="string">
  Advances `validating` → `in_progress` → `completed`. Becomes `cancelling` then `cancelled` after a cancel.
</ResponseField>

<ResponseField name="request_counts" type="object">
  `total`, `completed`, and `failed`. `total` is real from creation; `completed` and `failed` fill in as the executor runs.
</ResponseField>

<ResponseField name="output_file_id" type="string">
  Set when the batch completes: the [file](/api-v1/files) id holding one result line per request. Download its content to read the results.
</ResponseField>

Create returns immediately with `status: "validating"`; the batch runs in the background. Poll [retrieve](#retrieve-a-batch) until `status` is `completed`, then download the `output_file_id` content.

### Output file format [#output-file-format]

One line per request, in the OpenAI batch-output shape:

```json
{"id":"batch_req_01jy...","custom_id":"r1","response":{"status_code":200,"request_id":null,"body":{ "object":"chat.completion","...":"..." }},"error":null}
```

A failed request line carries a non-null `error` (`code`, `message`) and a null `response`.

## Retrieve a batch [#retrieve-a-batch]

```
GET https://api.zumik.ai/v1/batches/{batch_id}
```

<ParamField path="batch_id" type="string">
  The `batch_...` id.
</ParamField>

Returns the current batch object, including live `request_counts` and (once complete) `output_file_id`. `404` if it does not exist for this project.

## Cancel a batch [#cancel-a-batch]

```
POST https://api.zumik.ai/v1/batches/{batch_id}/cancel
```

Requests cancellation. A still-running batch (`validating` or `in_progress`) moves to `cancelling`, and the executor stops between requests, finalizing as `cancelled` with whatever it completed. A batch that already reached a terminal status keeps it. `404` if it does not exist for this project.

## Limits [#limits]

* A project may have at most 16 active batches at once; beyond that, create returns `429` / `quota_exceeded`.
* A single input file is capped at 50,000 requests; split larger jobs.
* Batches and their files are transient async I/O. Output files expire after a day, batch records after a week.

## Errors [#errors]

| HTTP | `code`            | When                                                                                                                                 |
| ---- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| 400  | (none)            | `input_file_id` is missing or unknown, `endpoint` is not `/v1/chat/completions`, or the input file is empty or over the request cap. |
| 401  | `invalid_api_key` | Missing or invalid bearer key.                                                                                                       |
| 404  | (none)            | The batch id does not exist for this project.                                                                                        |
| 429  | `quota_exceeded`  | Too many active batches for this project.                                                                                            |

See the full [error reference](/api-reference/errors).
