OpenAI Batch API: half price for anything that can wait a day
The OpenAI Batch API takes a file of up to 50,000 requests, runs them within 24 hours, and bills input and output at 50% of the standard price. It has its own rate limit pool, so a large job never slows your live traffic, and cached input is discounted on top: on gpt-6.1-sol a cached batch input token costs $0.05 per million instead of $2. The trade is plumbing: you upload a JSONL file, wait for a webhook, and match results back by ID, because they come back in any order.
- 50% off input and output on most models, results within 24 hours (often much sooner), and a separate rate limit pool from your synchronous traffic.
- One batch is one JSONL file: up to 50,000 requests, 200 MB, one model. Each line needs a unique
custom_id, because output order does not match input order. - Batch stacks with prompt caching. Put the shared instructions first in every request: on gpt-6.1-sol a cached input token in a batch is 40× cheaper than a standard one.
- Flex processing bills the same rates as a normal synchronous call with
service_tier: "flex". Use it when you want batch prices without the file and the wait. - A completed batch can still contain failed requests, and an expired one keeps the results it finished. Read the error file every time and requeue by
custom_id.
What the OpenAI Batch API is
The Batch API is an asynchronous way to send many API requests at once. Instead of calling the model and waiting for each answer, you upload a file of requests, OpenAI runs them when it has spare capacity, and you download a file of answers. In exchange for giving up the immediate answer, every token is billed at half price.
The requests themselves are ordinary: each line of the file is the same body you would send to /v1/responses, /v1/chat/completions, /v1/embeddings, /v1/completions, /v1/moderations, /v1/images/generations or /v1/images/edits. The model, the prompt and the output format do not change. Only the delivery does.
| Standard API | Batch API | |
|---|---|---|
| Price | List price | 50% of list, input and output |
| When you get the answer | Seconds | Within 24 hours, often much sooner |
| Rate limits | Per-model requests and tokens per minute | A separate pool: a queued-token limit per model |
| Streaming | Yes | No |
| Good for | Users waiting on a reply, agent loops | Evals, classification, extraction, embeddings, backfills |
The rule of thumb: if nobody is waiting on the answer, it should not be billed at the interactive price. Nightly evaluations, labelling a dataset, summarizing yesterday's support tickets, embedding a document archive and re-running a prompt change across past inputs all qualify.
How to use the Batch API, step by step
A batch takes four calls: write a JSONL file, upload it, create the batch, then download the results when it finishes. The example classifies support tickets with the official Python SDK.
1. Write one request per line
2 and 3. Upload the file and create the batch
4. Download the results and match them by custom_id
A batch moves through validating, in_progress and finalizing to completed. It ends in failed if the input file is invalid, expired if the 24 hours run out, or cancelled if you call client.batches.cancel(batch_id); cancelling takes up to 10 minutes while in-flight requests finish. The output file is deleted 30 days after the batch completes, so copy results somewhere you keep.
What the Batch API costs, and how it stacks with caching
Batch prices are exactly half of standard prices, and the cached input rate gets the same cut. Prompt caching is automatic on OpenAI models: a request that starts with the same long prefix as a recent one is billed at the cached rate for that prefix. In a batch where every request shares the same instructions, most of the input is that prefix.
| Per 1M tokens | Input | Cached input | Output |
|---|---|---|---|
| gpt-6.1-sol, standard | $2.00 | $0.10 | $10.00 |
| gpt-6.1-sol, batch | $1.00 | $0.05 | $5.00 |
| gpt-6-luna, standard | $0.10 | $0.01 | $0.50 |
| gpt-6-luna, batch | $0.05 | $0.005 | $0.25 |
Short-context prices (up to 272K input tokens) from OpenAI's pricing page on 2026-10-11. To see what your own prompt costs at these rates, paste it into the token counter.
A worked example: 100,000 tickets
Illustrative: classify 100,000 support tickets on gpt-6.1-sol. Each request carries 1,500 tokens of shared instructions and examples, 500 tokens of ticket, and returns 100 tokens. That is 200M input tokens and 10M output tokens.
| Setup | Input cost | Output cost | Total |
|---|---|---|---|
| Standard, no cache hits | $400 | $100 | $500 |
| Standard, instructions cached | $115 | $100 | $215 |
| Batch, no cache hits | $200 | $50 | $250 |
| Batch, instructions cached | $57.50 | $50 | $107.50 |
| Batch, cached, tickets trimmed to 250 tokens | $32.50 | $50 | $82.50 |
The cached rows assume every request hits the cache; real hit rates are lower, so treat them as the ceiling. Two things stand out. Caching and batching together cut the bill by almost 80%, and both need the same discipline: the shared part goes first and is byte-identical on every line. And once the instructions are cached, the variable part of each request is most of the input bill, which neither discount touches.
Batch API or Flex processing?
Flex processing gives you Batch API prices on a normal synchronous request. Set service_tier to "flex" and the request is billed at batch rates, with prompt caching on top. The trade is slower responses and occasional 429 Resource Unavailable errors, which you are not charged for. OpenAI's examples raise the client timeout to 15 minutes.
| Batch API | Flex processing | |
|---|---|---|
| Price | 50% of standard | Same as Batch |
| Shape | Upload a file, wait, download a file | One normal API call |
| Latency | Minutes to 24 hours | Slower than standard, still one request |
| Failure mode | Requests expire after 24 hours | 429 when capacity is short: retry later |
| Model support | Most models | Beta, fewer models (see the pricing page) |
| Best for | Tens of thousands of independent requests | Background jobs with steps that depend on each other |
Pick Flex when each step needs the previous answer, as in a background agent or a multi-step pipeline, or when the job is too small to be worth a file. Pick the Batch API when you have thousands of independent requests, want a separate rate limit pool, or need a model Flex does not support yet.
What breaks in production
The OpenAI forum threads that rank for this topic are mostly about the same handful of surprises. Each has a simple fix once you know it is there.
- Polling. You do not have to poll. Register a webhook endpoint in the dashboard and OpenAI sends
batch.completed,batch.failed,batch.expiredorbatch.cancelled; the SDK'sclient.webhooks.unwrap()verifies the signature. Keep a slow poll (every 10 to 30 minutes) as a fallback for missed deliveries. - Completed does not mean succeeded. Batch status is about the job, not the requests. A
completedbatch can have failed lines in the error file, and a request can return a 200 that is truncated or off-format. Validate each answer and requeue failures in a new batch. - Expiry keeps what finished. If 24 hours pass, finished requests stay in the output file and are billed; unfinished ones go to the error file with code
batch_expired. Read theircustom_ids and submit them again. - The queued-token limit. Each model has a cap on input tokens waiting across all your batches, shown in your organization's limits page. A batch that would exceed it fails validation, so split big jobs and submit the next part when one finishes. Count tokens before submitting rather than guessing.
- Results out of order. The output file is not in input order. Always map by
custom_id, and make it something you can join on (a ticket ID, a row key), not a line number. - One model per file, 200 MB per file. Mixing models fails validation. For images, a URL instead of base64 keeps the file small.
- "My batch results differ from my live results." The same prompt can give different answers on any two calls unless you pin the model snapshot and keep sampling settings identical. Batching does not change the model; check that the body really is the same.
- Conversations. Each line is independent: a batch cannot use the answer of one request in the next. Multi-turn work needs one batch per turn, or Flex.
Batch APIs at Anthropic and Google
The other two big providers sell the same deal: half price for work that can wait up to a day. The limits differ, which matters if you run the same job across providers.
| OpenAI Batch API | Anthropic Message Batches | Gemini Batch Mode | |
|---|---|---|---|
| Discount | 50% | 50% | 50% |
| Time limit | 24 hours | 24 hours (most finish within 1 hour) | 24-hour target |
| Size per batch | 50,000 requests or 200 MB | 100,000 requests or 256 MB | 2 GB input file |
| Stacks with caching | Yes, cached input at half the cached rate | Yes; cache hits are best effort | Yes, at standard caching rates |
| Results kept | 30 days | 29 days | See the docs |
Sources: Anthropic's batch processing docs and Gemini's Batch Mode docs. For Claude, batches often run longer than the 5-minute cache, so Anthropic suggests the 1-hour cache for shared context; the trade-offs are in our Anthropic prompt caching guide.
Batching halves the price; it does not shrink the prompt
The Batch API discounts every token you send, including the ones the model never needed. Batch jobs are where this adds up: they are large by definition, they often carry whole documents, retrieved passages or raw records per line, and nobody reads the prompts because nobody is waiting on them.
The order that keeps the most money: first remove what the model does not need from the variable part of each request (quoted email threads, signatures, boilerplate, irrelevant retrieved chunks, empty JSON fields), then put the shared instructions first so they cache, then batch. In the example above, trimming each ticket from 500 to 250 tokens saved another 23% after both discounts were already applied.
The full list of levers, from caching to model choice, is in the LLM cost optimization playbook. For retrieved context specifically, see RAG context compression.
Frequently asked questions
How much cheaper is the OpenAI Batch API?
50% cheaper than the standard API on input and output tokens for supported models. Cached input is halved too: on gpt-6.1-sol it is $0.05 per million tokens in a batch, against $0.10 cached and $2.00 uncached on the standard API.
How long does a batch take?
OpenAI commits to finishing within the 24-hour completion window, the only window available, and batches often finish much sooner. If the window runs out, the batch expires: finished requests are kept and billed, and unfinished ones are listed in the error file with the code batch_expired.
What are the Batch API limits?
Up to 50,000 requests and 200 MB per input file, one model per file, up to 2,000 batches created per hour, and a per-model limit on queued input tokens across all your batches. Embedding batches are capped at 50,000 inputs in total. There is no output-token limit.
Does the Batch API count against my rate limits?
No. Batch requests use a separate pool, so a large batch does not consume the requests or tokens per minute your live traffic needs. The batch pool has its own limit on queued input tokens per model.
Does prompt caching work with the Batch API?
Yes. OpenAI's pricing page lists a cached input rate for batch requests at half the standard cached rate. Put the instructions shared by every request at the start of each body and keep them byte-identical so they can hit the cache.
Can I use the Batch API for multi-turn conversations?
Not within one batch: every line is an independent request, so one answer cannot feed the next. Send one batch per turn, or use Flex processing, which gives batch prices on ordinary synchronous calls.
Why are my batch results in a different order?
The output file is not guaranteed to follow the input order. Each output line carries the custom_id you set on the request, so match results by custom_id rather than by position.
Keep reading

LLM Cost Optimization: 8 Levers That Cut 50–90%
LLM cost optimization in 8 levers with 2026 prices: cut input context, prompt caching (90% off), batching (50% off), routing and output caps. Worked examples.

Anthropic Prompt Caching (Claude): What It Costs
Anthropic prompt caching bills a repeated prefix at 10% of the input price or less. How cache_control works, when it pays off, and what silently breaks it.

RAG Context Compression vs Reranking
RAG context compression removes redundant, off-topic and low-value chunks before the model call. How it differs from retrieval and reranking, with math.