# Batch API

Upload a JSONL file of chat completions, create a batch and collect the results when it finishes.

## Batch

The Batch API runs many chat completions without keeping a connection open, the same way
OpenAI's Batch API does: upload a JSONL file, create a batch from it, and download the results
when it finishes, within 24h.

Each line of the file is one request:

```json
{"custom_id": "req-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "deepseek/deepseek-v4.1-flash", "messages": [{"role": "user", "content": "Hello from OpenScale"}]}}
```

The `body` is a normal chat completion request without `stream`, and every line uses the same
model. Every line is checked when the batch is created, with the same checks as
`POST /v1/chat/completions`: a file with any invalid line gives a `failed` batch whose `errors`
list the lines, and nothing runs.

```bash
curl https://api.openscale.so/v1/files -H "Authorization: Bearer $OPENSCALE_API_KEY"   -F purpose=batch -F file=@requests.jsonl
curl https://api.openscale.so/v1/batches -H "Authorization: Bearer $OPENSCALE_API_KEY"   -H "Content-Type: application/json"   -d '{"input_file_id": "file-...", "endpoint": "/v1/chat/completions", "completion_window": "24h"}'
```

When the batch is `completed`, `output_file_id` holds the successful responses and
`error_file_id` the failed, expired and cancelled requests, one JSON line each with its
`custom_id`. Download them with `GET /files/{file_id}/content`.

| Limit | Value |
| --- | --- |
| File size | 20 MB |
| Requests per file | 10,000 |
| Requests waiting across an account's batches | 50,000 |
| `custom_id` length | 512 characters |
| Completion window | 24h |
| Files, batches and results kept | 30 days |

Batches bill your balance, so they need a pay-as-you-go key: a plan key
gets `400` with `code: batch_not_in_plan`. Files and batches belong to your account, so every
key on it can read them. With zero data retention on, new files and batches answer `400` with
`code: zero_data_retention`.

## Endpoints

- [List files](#list-files): `GET https://api.openscale.so/v1/files`
- [Upload a file](#upload-a-file): `POST https://api.openscale.so/v1/files`
- [Retrieve a file](#retrieve-a-file): `GET https://api.openscale.so/v1/files/{file_id}`
- [Delete a file](#delete-a-file): `DELETE https://api.openscale.so/v1/files/{file_id}`
- [Download file content](#download-file-content): `GET https://api.openscale.so/v1/files/{file_id}/content`
- [List batches](#list-batches): `GET https://api.openscale.so/v1/batches`
- [Create a batch](#create-a-batch): `POST https://api.openscale.so/v1/batches`
- [Retrieve a batch](#retrieve-a-batch): `GET https://api.openscale.so/v1/batches/{batch_id}`
- [Cancel a batch](#cancel-a-batch): `POST https://api.openscale.so/v1/batches/{batch_id}/cancel`

### List files

`GET https://api.openscale.so/v1/files`

Your account's files, newest first. Filter with `purpose` (`batch` or `batch_output`).

#### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `purpose` | string | No | One of `batch`, `batch_output`. |
| `limit` | integer | No | From 1 to 100. Default `20`. |
| `after` | string | No | The id of the last object on the previous page. |

#### Example request

```bash
curl https://api.openscale.so/v1/files \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: A list of files.

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | Always `list`. |
| `data` | array of object |  |
| `data[].id` | string | Example: `file-example`. |
| `data[].object` | string | Always `file`. |
| `data[].bytes` | integer |  |
| `data[].created_at` | integer |  |
| `data[].expires_at` | integer | Unix seconds when the file is deleted, 30 days after upload. |
| `data[].filename` | string |  |
| `data[].purpose` | string | `batch` for uploads, `batch_output` for results. One of `batch`, `batch_output`. |
| `data[].status` | string | Always `processed`. |
| `first_id` | string or null |  |
| `last_id` | string or null |  |
| `has_more` | boolean |  |

### Upload a file

`POST https://api.openscale.so/v1/files`

Upload a JSONL batch input file as `multipart/form-data` with the fields `file` and `purpose` (`batch`). Up to 20 MB and 10,000 requests.

#### Request body

Sent as `multipart/form-data`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file` | string | Yes | The JSONL file, one request per line. |
| `purpose` | string | Yes | Always `batch`. |

#### Example request

```bash
curl https://api.openscale.so/v1/files \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -F purpose=batch \
  -F file=@requests.jsonl
```

#### Response

`200`: The file.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `file-example`. |
| `object` | string | Always `file`. |
| `bytes` | integer |  |
| `created_at` | integer |  |
| `expires_at` | integer | Unix seconds when the file is deleted, 30 days after upload. |
| `filename` | string |  |
| `purpose` | string | `batch` for uploads, `batch_output` for results. One of `batch`, `batch_output`. |
| `status` | string | Always `processed`. |

```json
{
  "id": "file-example",
  "object": "file",
  "bytes": 1204,
  "created_at": 1790078324,
  "expires_at": 1792670324,
  "filename": "requests.jsonl",
  "purpose": "batch",
  "status": "processed"
}
```

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | Invalid upload; `param` names the field. `zero_data_retention` when the account has zero data retention on. |
| 413 | The file is larger than the limit. |

### Retrieve a file

`GET https://api.openscale.so/v1/files/{file_id}`

A file's metadata.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file_id` | string | Yes |  |

#### Example request

```bash
curl https://api.openscale.so/v1/files/$FILE_ID \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The file.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `file-example`. |
| `object` | string | Always `file`. |
| `bytes` | integer |  |
| `created_at` | integer |  |
| `expires_at` | integer | Unix seconds when the file is deleted, 30 days after upload. |
| `filename` | string |  |
| `purpose` | string | `batch` for uploads, `batch_output` for results. One of `batch`, `batch_output`. |
| `status` | string | Always `processed`. |

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No file with this id in your account. |

### Delete a file

`DELETE https://api.openscale.so/v1/files/{file_id}`

Delete a file at once.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file_id` | string | Yes |  |

#### Example request

```bash
curl -X DELETE https://api.openscale.so/v1/files/$FILE_ID \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: Deleted.

```json
{
  "id": "file-example",
  "object": "file",
  "deleted": true
}
```

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No file with this id in your account. |

### Download file content

`GET https://api.openscale.so/v1/files/{file_id}/content`

The file as uploaded, or a batch's results: one JSON line per request with its `custom_id` and either a `response` (`status_code` and `body`) or an `error`.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file_id` | string | Yes |  |

#### Example request

```bash
curl https://api.openscale.so/v1/files/$FILE_ID/content \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The file content.

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No file with this id in your account. |

### List batches

`GET https://api.openscale.so/v1/batches`

Your account's batches, newest first.

#### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | No | From 1 to 100. Default `20`. |
| `after` | string | No | The id of the last object on the previous page. |

#### Example request

```bash
curl https://api.openscale.so/v1/batches \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: A list of batches.

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | Always `list`. |
| `data` | array of object |  |
| `data[].id` | string | Example: `batch_example`. |
| `data[].object` | string | Always `batch`. |
| `data[].endpoint` | string | Always `/v1/chat/completions`. |
| `data[].model` | string or null |  |
| `data[].errors` | object or null | For a `failed` batch: the invalid lines of the input file, each with its line number, code and message. |
| `data[].input_file_id` | string |  |
| `data[].completion_window` | string | Always `24h`. |
| `data[].status` | string | One of `failed`, `in_progress`, `completed`, `expired`, `cancelling`, `cancelled`. |
| `data[].output_file_id` | string or null | Results of the requests that succeeded. |
| `data[].error_file_id` | string or null | Failed, expired and cancelled requests. |
| `data[].created_at` | integer |  |
| `data[].expires_at` | integer or null |  |
| `data[].completed_at` | integer or null |  |
| `data[].request_counts` | object |  |
| `data[].metadata` | object or null |  |
| `first_id` | string or null |  |
| `last_id` | string or null |  |
| `has_more` | boolean |  |

### Create a batch

`POST https://api.openscale.so/v1/batches`

Start a batch from an uploaded file. Every line is checked first: a file with an invalid line gives a `failed` batch that lists the errors, and nothing runs. Needs a pay-as-you-go key.

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `input_file_id` | string | Yes | A file uploaded with `purpose: batch`. |
| `endpoint` | string | Yes | Always `/v1/chat/completions`. |
| `completion_window` | string | Yes | Always `24h`. |
| `metadata` | object | No | Up to 16 string pairs (keys up to 64 characters, values up to 512). |

#### Example request

```bash
curl https://api.openscale.so/v1/batches \
  -H "Authorization: Bearer $OPENSCALE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "input_file_id": "file-example",
  "endpoint": "/v1/chat/completions",
  "completion_window": "24h"
}'
```

#### Response

`200`: The batch.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `batch_example`. |
| `object` | string | Always `batch`. |
| `endpoint` | string | Always `/v1/chat/completions`. |
| `model` | string or null |  |
| `errors` | object or null | For a `failed` batch: the invalid lines of the input file, each with its line number, code and message. |
| `input_file_id` | string |  |
| `completion_window` | string | Always `24h`. |
| `status` | string | One of `failed`, `in_progress`, `completed`, `expired`, `cancelling`, `cancelled`. |
| `output_file_id` | string or null | Results of the requests that succeeded. |
| `error_file_id` | string or null | Failed, expired and cancelled requests. |
| `created_at` | integer |  |
| `expires_at` | integer or null |  |
| `completed_at` | integer or null |  |
| `request_counts` | object |  |
| `request_counts.total` | integer |  |
| `request_counts.completed` | integer |  |
| `request_counts.failed` | integer |  |
| `metadata` | object or null |  |

```json
{
  "id": "batch_example",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "model": "deepseek/deepseek-v4.1-flash",
  "errors": null,
  "input_file_id": "file-example",
  "completion_window": "24h",
  "status": "in_progress",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1790078324,
  "expires_at": 1790164724,
  "completed_at": null,
  "request_counts": {
    "total": 2,
    "completed": 0,
    "failed": 0
  },
  "metadata": null
}
```

#### Errors

| Status | Meaning |
| --- | --- |
| 400 | Invalid request; `param` names the field. `batch_not_in_plan` for a plan key, `zero_data_retention` when the account has zero data retention on. |
| 401 | Missing or invalid API key. |

### Retrieve a batch

`GET https://api.openscale.so/v1/batches/{batch_id}`

A batch, with live request counts while it runs.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `batch_id` | string | Yes |  |

#### Example request

```bash
curl https://api.openscale.so/v1/batches/$BATCH_ID \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The batch.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `batch_example`. |
| `object` | string | Always `batch`. |
| `endpoint` | string | Always `/v1/chat/completions`. |
| `model` | string or null |  |
| `errors` | object or null | For a `failed` batch: the invalid lines of the input file, each with its line number, code and message. |
| `input_file_id` | string |  |
| `completion_window` | string | Always `24h`. |
| `status` | string | One of `failed`, `in_progress`, `completed`, `expired`, `cancelling`, `cancelled`. |
| `output_file_id` | string or null | Results of the requests that succeeded. |
| `error_file_id` | string or null | Failed, expired and cancelled requests. |
| `created_at` | integer |  |
| `expires_at` | integer or null |  |
| `completed_at` | integer or null |  |
| `request_counts` | object |  |
| `request_counts.total` | integer |  |
| `request_counts.completed` | integer |  |
| `request_counts.failed` | integer |  |
| `metadata` | object or null |  |

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No batch with this id in your account. |

### Cancel a batch

`POST https://api.openscale.so/v1/batches/{batch_id}/cancel`

Cancel the requests that have not started. The batch moves to `cancelling`, then `cancelled`; finished requests keep their results.

#### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `batch_id` | string | Yes |  |

#### Example request

```bash
curl -X POST https://api.openscale.so/v1/batches/$BATCH_ID/cancel \
  -H "Authorization: Bearer $OPENSCALE_API_KEY"
```

#### Response

`200`: The batch.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Example: `batch_example`. |
| `object` | string | Always `batch`. |
| `endpoint` | string | Always `/v1/chat/completions`. |
| `model` | string or null |  |
| `errors` | object or null | For a `failed` batch: the invalid lines of the input file, each with its line number, code and message. |
| `input_file_id` | string |  |
| `completion_window` | string | Always `24h`. |
| `status` | string | One of `failed`, `in_progress`, `completed`, `expired`, `cancelling`, `cancelled`. |
| `output_file_id` | string or null | Results of the requests that succeeded. |
| `error_file_id` | string or null | Failed, expired and cancelled requests. |
| `created_at` | integer |  |
| `expires_at` | integer or null |  |
| `completed_at` | integer or null |  |
| `request_counts` | object |  |
| `request_counts.total` | integer |  |
| `request_counts.completed` | integer |  |
| `request_counts.failed` | integer |  |
| `metadata` | object or null |  |

#### Errors

| Status | Meaning |
| --- | --- |
| 404 | No batch with this id in your account. |
| 409 | The batch has already finished (`batch_not_cancellable`). |

## More of the API reference

- [API reference](https://openscale.so/docs): base URLs, authentication and a first request.
- [Chat Completions](https://openscale.so/docs/chat-completions): POST /v1/chat/completions: the OpenAI Chat Completions format, streamed or not, with reasoning and tools.
- [Responses](https://openscale.so/docs/responses): POST /v1/responses: the OpenAI Responses format with stored state, background runs and enforced tool rules.
- [Conversations](https://openscale.so/docs/conversations): Keep the turns of a Responses conversation on our side and continue it by id.
- [Prompts](https://openscale.so/docs/prompts): Save instructions, messages and settings once, then use them by id in the Responses API.
- [Anthropic Messages](https://openscale.so/docs/messages): POST /anthropic/v1/messages: the Anthropic Messages format, for Claude Code and Anthropic SDKs.
- [Models and pricing](https://openscale.so/docs/models): Model ids, prices per 1M tokens, context windows, output limits, versions and the live catalog endpoint.
- [Errors](https://openscale.so/docs/errors): The error shape, every status code and code, and which ones to retry.
- [Rate limits](https://openscale.so/docs/rate-limits): Requests and tokens per minute, how a 429 tells you when to retry, and how to get more.
- [Data retention](https://openscale.so/docs/data-retention): What is stored, for how long, how to delete it, and zero data retention.
- [Interactive reference](https://openscale.so/docs/reference): try every endpoint in the browser.
- [OpenAPI document](https://openscale.so/openapi.json): the whole API as OpenAPI 3.1.
