Skip to main content
In this tutorial you’ll upload a JSONL file of chat completion requests, run it as a single batch job, and match the responses back to your inputs. With the CLI, the whole flow takes three commands:
The steps below break down this flow and show the SDK and REST equivalents for each part.

Requirements

Before you begin, make sure you have:

Step 1: Prepare a JSONL input file

Each line of the JSONL file is one request with two fields: a unique custom_id you choose (up to 64 characters), and a body matching the schema of the endpoint you’re calling. Every line runs independently, and its output carries the same custom_id, which is how you’ll match results to inputs at the end. Save the following as batch_input.jsonl:
batch_input.jsonl
Audio requests add a third field, method. See Run an audio transcription batch.
Each line must be under 10 MB, including any inline base64 payloads (a single high-resolution image embedded as a data:image/...;base64, URL can exceed it). Oversized lines aren’t caught during validation and fail with error reading input file. Reference images by hosted URL instead of inlining them, or resize and compress them before encoding.

Step 2: Upload the file

Upload the JSONL file with purpose="batch-api". The response includes the file id you’ll pass to the batch job in the next step. CLI users can skip this step: tg batches submit accepts a local file path and uploads it for you (see the next step).
The SDK examples infer the file name from the local path. When calling the REST API directly, include file_name in the multipart form. See the file upload reference for the full request shape.

Step 3: Create the batch

Create the batch by passing the file id from step 2 and the endpoint each request runs against: /v1/chat/completions for chat completions, or --api chat.completions in the CLI. For audio, see Run an audio transcription batch.
In the SDKs, batches.create() returns a wrapper with the batch object at .job. batches.retrieve() (used in the next step) returns the batch object directly.

Step 4: Poll for completion

The job moves through VALIDATING and IN_PROGRESS, and finally to a terminal state: COMPLETED, FAILED, EXPIRED, or CANCELLED. Poll every 30 to 60 seconds (tighter loops will likely cause you to hit rate limits).
progress tracks the percentage of requests completed, from 0 to 100. It may stay at 0 while the job is in VALIDATING.
Most batches under 1,000 requests finish in minutes. The 24-hour completion window is a maximum, not a typical wait.

Step 5: Retrieve the results

When the job reaches COMPLETED, the batch object includes an output_file_id. Download that file to get one JSON result per line. Results aren’t guaranteed to be in input order, so match them to inputs using the custom_id field.
A successful output line looks like:
Failed requests land in a separate file referenced by error_file_id. Always check it: a batch can be COMPLETED and still contain failures. The CLI’s download saves the error file automatically (here as batch_output.errors.jsonl). See retrieve results and error files.

Run an audio transcription batch

The batch API also supports /v1/audio/transcriptions and /v1/audio/translations (for example, with openai/whisper-large-v3). Upload, poll, and retrieve work exactly as above. Two things change: 1. Each JSONL line must include "method": "FILE". This tells the worker to send the request as multipart/form-data, which the audio endpoints require. Without it, every line fails with Content-Type must be multipart/form-data in the error file.
audio_batch.jsonl
body.file is a publicly reachable URL for the audio clip. The worker fetches it at run time. Optional fields such as response_format, language, and prompt pass through to the underlying API. See the audio transcriptions reference for the full schema. 2. Pass the audio endpoint when creating the batch.
A successful output line looks like:
For /v1/audio/translations, swap the endpoint and use a translation-capable model. The JSONL line shape is the same.

Complete script

The full flow as a CLI session or a Python program:

Next steps