> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zerogpu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# JSONL Format

> Mandatory line schemas for input, output, and error JSONL files.

Every batch is driven by three JSONL files:

| File       | Direction         | Purpose                                                  |
| ---------- | ----------------- | -------------------------------------------------------- |
| **Input**  | You upload it     | One request per line; describes what the batch should do |
| **Output** | Service writes it | One result per successful line                           |
| **Error**  | Service writes it | One record per failed line                               |

This page is the canonical specification for all three line schemas.

## Input JSONL

The input is a JSONL file (one JSON object per line, separated by `\n`). You
upload it via [`POST /v1/files`](/api-reference/upload-file)
with `purpose=batch`, then reference its ID when you call
[`POST /v1/batches`](/api-reference/create-batch).

### Line schema

```json theme={null}
{
  "custom_id": "request-1",
  "method":    "POST",
  "url":       "/v1/chat/completions",
  "body":      { /* endpoint-specific payload */ }
}
```

| Field       | Type   | Required | Description                                                                                                                                                                                                           |
| ----------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `custom_id` | string | **Yes**  | Your identifier for this request. Must be non-empty and **unique within the batch**. Returned verbatim in the corresponding output or error line so you can match results to inputs.                                  |
| `method`    | string | **Yes**  | HTTP method, case-insensitive. Only `POST` (any case) is accepted today.                                                                                                                                              |
| `url`       | string | **Yes**  | Must be `/v1/chat/completions`, the [only batchable endpoint](/docs/batch/supported-endpoints). **All lines in a single batch must share the same `url`**, and it must match the `endpoint` field on the create call. |
| `body`      | object | **Yes**  | The request body that would be sent to the synchronous endpoint. See [Supported endpoints](/docs/batch/supported-endpoints) for the shape. The field `stream: true` is rejected.                                      |

### Validation rules

The service performs all of the following checks at `POST /v1/batches` time
(not at upload time). Failure returns `400` with the offending `line`:

* Each line must be valid JSON (not arrays, not primitives, must be an object).
* `custom_id` non-empty string, unique across the whole batch.
* `method` is case-insensitive; the upper-cased value must equal `"POST"`.
* `url` must be exactly `/v1/chat/completions` on every line in the file.
  Other ZeroGPU sync routes are rejected here.
* `body` must be a non-empty object.
* `body.stream === true` is rejected (streaming is not supported in batch
  mode).
* Per-line size ≤ **1 MB**.
* Total file size ≤ **200 MB**.
* Total line count ≤ **50,000**.
* The file must contain at least one line.

### Format details

* Lines are separated by `\n` (LF). A trailing newline is allowed.
* Blank lines are skipped.
* UTF-8 encoding is required.
* The `url` value matches the `endpoint` field on the create request
  **exactly**, no trailing slash, no query string, no protocol/host prefix.

### Example, chat completions

```jsonl theme={null}
{"custom_id":"req-1","method":"POST","url":"/v1/chat/completions","body":{"model":"<model-id>","messages":[{"role":"user","content":"What is the capital of France?"}]}}
{"custom_id":"req-2","method":"POST","url":"/v1/chat/completions","body":{"model":"<model-id>","messages":[{"role":"user","content":"What is the capital of Germany?"}]}}
{"custom_id":"req-3","method":"POST","url":"/v1/chat/completions","body":{"model":"<model-id>","messages":[{"role":"user","content":"What is the capital of Italy?"}]}}
```

See [Supported endpoints](/docs/batch/supported-endpoints) for the
`/v1/chat/completions` body schema.

***

## Output JSONL

When a batch reaches `status: "completed"`, the service writes the successful
results to a file referenced by `output_file_id` on the Batch object. You
download it via
[`GET /v1/files/{file_id}/content`](/api-reference/download-file).

Only lines that received a **2xx** response from the underlying endpoint
appear in the output file. Failed lines go to the error file (see below).

The order of lines in the output file is **not guaranteed** to match the
order of lines in the input file, match results to inputs by `custom_id`.

### Line schema

```json theme={null}
{
  "id":        "batch_req_abc123...",
  "custom_id": "request-1",
  "response": {
    "status_code": 200,
    "request_id":  "req_xyz789...",
    "body":        { /* the endpoint's normal response body */ }
  }
}
```

| Field                  | Type           | Description                                                                                                                                                                          |
| ---------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                   | string         | Line-level identifier (prefix `batch_req_`). Stable across retries.                                                                                                                  |
| `custom_id`            | string         | Echo of the `custom_id` you supplied on the input line.                                                                                                                              |
| `response.status_code` | integer        | HTTP status from the underlying endpoint (will always be 2xx in this file).                                                                                                          |
| `response.request_id`  | string \| null | The synchronous request id (`req_…`) of the underlying call to the orchestration API. Useful for cross-referencing logs. `null` if the upstream service didn't surface a request id. |
| `response.body`        | object         | The same JSON body the synchronous endpoint would have returned for this request.                                                                                                    |

See [Supported endpoints](/docs/batch/supported-endpoints) for the exact
`response.body` shape (OpenAI `chat.completion`).

### Example, chat completions output

```jsonl theme={null}
{"id":"batch_req_a","custom_id":"req-1","response":{"status_code":200,"request_id":"req_xyz","body":{"id":"chatcmpl-abc","object":"chat.completion","created":1736295000,"model":"<model-id>","choices":[{"index":0,"message":{"role":"assistant","content":"Paris."},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14},"system_fingerprint":null}}}
{"id":"batch_req_b","custom_id":"req-2","response":{"status_code":200,"request_id":"req_uvw","body":{"id":"chatcmpl-def","object":"chat.completion","created":1736295001,"model":"<model-id>","choices":[{"index":0,"message":{"role":"assistant","content":"Berlin."},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14},"system_fingerprint":null}}}
```

### Reading the output

```python theme={null}
import json

with open("output.jsonl") as f:
    for line in f:
        record = json.loads(line)
        custom_id = record["custom_id"]
        body      = record["response"]["body"]
        answer    = body["choices"][0]["message"]["content"]
        print(f"{custom_id}: {answer}")
```

***

## Error JSONL

When a batch reaches `status: "completed"` (or `expired`) and at least one
line failed, the service writes the failures to a file referenced by
`error_file_id`. Successful lines go to the output file; the two are
disjoint.

### Line schema

```json theme={null}
{
  "id":        "batch_req_def456...",
  "custom_id": "request-5",
  "response":  null,
  "error": {
    "code":    "invalid_request_error",
    "message": "[legacy:http_400] request body is invalid",
    "param":   "model"
  }
}
```

| Field           | Type           | Description                                                                                                                             |
| --------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | string         | Line-level identifier (prefix `batch_req_`).                                                                                            |
| `custom_id`     | string         | Echo of the `custom_id` you supplied on the input line.                                                                                 |
| `response`      | null           | Always `null` on error lines. Mirrors OpenAI's `{id, custom_id, response, error}` discriminator so SDK type-guards work unchanged.      |
| `error.code`    | string         | OpenAI semantic error code. See the table below.                                                                                        |
| `error.message` | string         | Human-readable description. The original ZeroGPU internal code is preserved as a `[legacy:<old>] ` prefix on this string for forensics. |
| `error.param`   | string \| null | Name of the offending parameter when known.                                                                                             |

Each error line carries an `error.code` (`invalid_request_error`,
`internal_error`, `batch_cancelled`, and so on). The full list, what triggers
each, and how to recover is in the [Errors reference](/docs/batch/errors).

### Example, error file

```jsonl theme={null}
{"id":"batch_req_e1","custom_id":"req-5","response":null,"error":{"code":"invalid_request_error","message":"[legacy:http_400] {\"message\":\"messages: missing required field\",\"type\":\"invalid_request_error\"}","param":null}}
{"id":"batch_req_e2","custom_id":"req-9","response":null,"error":{"code":"internal_error","message":"[legacy:retries_exhausted] Line failed after 4 attempts: orchestration-api returned 503: upstream timeout","param":null}}
{"id":"batch_req_e3","custom_id":"req-12","response":null,"error":{"code":"internal_error","message":"[legacy:dlq_exhausted] Message exhausted main-queue retries and was routed to the DLQ","param":null}}
```

### Reading the error file

```python theme={null}
import json

with open("errors.jsonl") as f:
    for line in f:
        record = json.loads(line)
        line_id   = record["id"]            # batch_req_…
        custom_id = record["custom_id"]
        code      = record["error"]["code"]  # OpenAI semantic code
        msg       = record["error"]["message"]
        print(f"FAIL {custom_id} ({line_id}) [{code}]: {msg}")
```

***

## Matching outputs and errors back to inputs

The output and error files together account for **all** lines in your input
file, but their order is not preserved and they may be split across the two
files. To reconstruct results for your dataset:

```python theme={null}
import json

results = {}                                # custom_id -> result-or-error
with open("output.jsonl") as f:
    for line in f:
        rec = json.loads(line)
        results[rec["custom_id"]] = ("ok", rec["response"]["body"])
with open("errors.jsonl") as f:
    for line in f:
        rec = json.loads(line)
        results[rec["custom_id"]] = ("err", rec["error"])

# now iterate your original inputs and look up by custom_id
```

If a `custom_id` you submitted is missing from both files, the batch is not
complete yet, re-check `status` and download again.

<Tip>
  **Always match by <code>custom\_id</code>**

  Output and error lines are <em>not</em> guaranteed to appear in input
  order. Index your inputs by <code>custom\_id</code> in your code and look
  results up by that key.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Supported endpoints →" href="/docs/batch/supported-endpoints">
    Body and response shape for `/v1/chat/completions`.
  </Card>

  <Card title="Quickstart →" href="/docs/batch/getting-started">
    Full end-to-end walkthrough in curl and Python.
  </Card>

  <Card title="Errors reference →" href="/docs/batch/errors">
    Every JSONL validation message and error code, with recovery guidance.
  </Card>
</CardGroup>
