Skip to content

Batch API

Run a large set of chat completion or embedding requests asynchronously, at a lower price than the synchronous API, through the OpenAI Batch API shape.

A batch is created from a file of requests, runs without a connection held open, and is read back from the result files it produces — exactly the OpenAI workflow, so the official OpenAI SDKs work by changing the base URL.

Why Choose the Batch API?

  • Lower Price per Token
    Batched requests are billed at the published batch rate, well below the on-demand rate for the same model.

  • Drop-in OpenAI Compatibility
    client.batches.create(...) and client.batches.retrieve(...) work unchanged, as does the JSONL input format.

  • No Connection to Hold
    Submit and walk away. Results stay readable through the Files API once the batch ends.

  • Private AWS Backend
    Requests and results are stored in your own S3 buckets — no traffic to third-party endpoints.

Available Endpoints

Endpoint Method What It Does MCP Tool
/v1/batches POST Create a batch from an uploaded file openai_batch
/v1/batches GET List batches, newest first openai_batch_list
/v1/batches/{batch_id} GET Retrieve a batch and its counters openai_batch_get
/v1/batches/{batch_id}/cancel POST Cancel a batch that is still running openai_batch_cancel

Feature Compatibility

Feature Status Notes
Creation
input_file_id Must be uploaded with purpose="batch"
endpoint /v1/chat/completions and /v1/embeddings
completion_window 24h, as upstream
metadata Up to 16 key-value pairs, returned on every read
output_expires_after anchor: "created_at" and 1 hour to 30 days, counted from the moment the result files are written; omit it to keep them until deleted
Per-request body
messages, max_tokens, sampling Same parameters as Chat Completions
response_format json_object Full support
tools / tool_choice / functions Refused when the batch is created — tool use is not available in a batch
response_format json_schema Refused when the batch is created
stream A batch has nothing to stream to
n above 1 Send one request per completion instead
prompt_cache_key / prompt_cache_breakpoint Accepted and ignored — a batch reads and writes no prompt cache, and the request is answered without one
input, dimensions Same parameters as Embeddings, one input per request
encoding_format base64 Refused when the batch is created — batched vectors come back as numbers
Lifecycle
Retrieve / poll validatingin_progressfinalizingcompleted
Cancel cancelling then cancelled; requests already answered stay in output_file_id, and a batch that has ended is unchanged
List batches Newest first, with an after cursor
output_file_id / error_file_id Readable through the Files API
usage Token totals, reported once the batch ends
finalizing status Reported with finalizing_at while the results of a batch whose requests have run are being assembled; completed follows once they are readable

Legend:

  • Supported — Fully compatible with OpenAI API
  • Partial — Supported with limitations
  • Unsupported — Not available in this implementation

Content Guardrails and Batches

A request that a guardrail would apply to is refused rather than run unguarded. Send those requests without batching.

Prompt Caching and Batches

Batched requests neither read nor write a prompt cache, on any model. A request carrying a cache hint is still accepted and answered — the hint is dropped rather than the request — so a batch reports no cached tokens in usage.input_tokens_details. Nothing is lost by leaving the hint in: batched requests are already billed at the batch rate, and the cache discount was never available at that rate.

Model Support

Any chat or embedding model available for batch inference in your configured Amazon Bedrock regions can be used — the same identifiers as Chat Completions and Embeddings. To shortlist them, call search_models with route=openai_chat_completion&batch=true, or route=openai_embedding&batch=true for embeddings; each entry also carries a batch field.

The shortlist is a hint, not a rule

batch is advertised on a best-effort basis and never used to reject a request. A model it does not advertise — or says nothing about — may still run a batch, so submit the batch rather than ruling the model out; the answer you get back is the authoritative one.

A model that cannot serve batched requests is refused when the batch is created, naming the model. A model this deployment normally serves through another Amazon Bedrock endpoint is batched under the identifier the batch endpoint knows it by, so it needs nothing from you.

Workflow

1. Upload the requests

One JSON object per line, uploaded with purpose="batch". Every line names the same model, targets the same endpoint, carries a unique custom_id, and holds the request itself in body.

{"custom_id": "req-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "amazon.nova-micro-v1:0", "messages": [{"role": "user", "content": "Summarize: ..."}]}}
{"custom_id": "req-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "amazon.nova-micro-v1:0", "messages": [{"role": "user", "content": "Summarize: ..."}]}}

Embedding requests are written the same way, against /v1/embeddings:

{"custom_id": "doc-1", "method": "POST", "url": "/v1/embeddings", "body": {"model": "amazon.titan-embed-text-v2:0", "input": "First passage of the corpus"}}
{"custom_id": "doc-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "amazon.titan-embed-text-v2:0", "input": "Second passage of the corpus"}}

Both samples above are abridged: a real file needs at least 100 requests, the minimum a batch carries.

from openai import OpenAI

client = OpenAI(base_url="https://your-host/v1", api_key="...")

requests_file = client.files.create(file=open("requests.jsonl", "rb"), purpose="batch")

2. Create the batch

batch = client.batches.create(
    input_file_id=requests_file.id,
    endpoint="/v1/chat/completions",
    completion_window="24h",
)

Example request (curl):

curl -X POST "https://your-host/v1/batches" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input_file_id": "file-06fvfg3lbdqarbad8kbo55g0sg5h3s4a",
    "endpoint": "/v1/chat/completions",
    "completion_window": "24h"
  }'

Example response:

{
  "id": "batch_06fvfg3lbdqarbad8kbo55g0sg5h3s4a",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "input_file_id": "file-06fvfg3lbdqarbad8kbo55g0sg5h3s4a",
  "completion_window": "24h",
  "status": "validating",
  "created_at": 1786568013,
  "expires_at": 1786654413,
  "request_counts": {"total": 100, "completed": 0, "failed": 0},
  "model": "amazon.nova-micro-v1:0"
}

3. Poll until it ends

batch = client.batches.retrieve(batch.id)
print(batch.status, batch.request_counts)

4. Read the results

results = client.files.content(batch.output_file_id).text

Each line pairs a custom_id with the completion it produced:

{"id": "batch_req_9f2c...", "custom_id": "req-1", "response": {"status_code": 200, "request_id": "batch_req_9f2c...", "body": {"id": "chatcmpl-req-1", "object": "chat.completion", "choices": [{"index": 0, "message": {"role": "assistant", "content": "..."}, "finish_reason": "stop"}], "usage": {"prompt_tokens": 22, "completion_tokens": 9, "total_tokens": 31}}}, "error": null}

Requests that failed are collected in a separate file, named by error_file_id.

Expiring the result files

Result files are kept until deleted, and are billed as stored objects meanwhile. Pass output_expires_after when creating the batch to have both files expire on their own — between 1 hour and 30 days, counted from the moment they are written:

batch = client.batches.create(
    input_file_id=requests_file.id,
    endpoint="/v1/chat/completions",
    completion_window="24h",
    output_expires_after={"anchor": "created_at", "seconds": 7 * 24 * 3600},
)

Results Are Not in Request Order

Output lines may come back in any order, as upstream also warns. Match a result to its request with custom_id, never with the line number.

Limits

Limit Value
Minimum requests per batch 100 (default quota)
Maximum requests per batch 50,000
Maximum input file size 200 MB
custom_id length 64 characters
Distinct models per input file 1 (upstream rule)
Processing window 24 hours from creation

A batch below the minimum, or past any of these caps, is refused when it is created and the message names the shortfall, rather than accepted and failed later.

The 100-request minimum is a quota default

100 is the default of the Amazon Bedrock quota Minimum number of records per batch inference job, which is set per model and adjustable for some of them — see Amazon Bedrock quotas. The gateway checks against that default, not against your account's own value, so a raised quota is enforced by Amazon Bedrock rather than here — a batch of 150 clears this check and is then refused by the backend — and a lowered one is not usable: fewer than 100 requests is still refused here.

Prerequisites

The Batch API is disabled until the deployment declares an AWS IAM service role that Amazon Bedrock assumes to read the requests and write the results:

The permissions the role and the server need are listed in IAM Permissions. While the role is unset, every batch endpoint answers 503.

Billing

Batched requests are billed at the published batch rate for the model, roughly half the on-demand rate. Usage is recorded once, when the batch ends. See Cost Management.

See Also