Skip to content

OpenAI compatibility

BatchRouter exposes an OpenAI-Batch-compatible API under /v1/openai/v1/. If you already have code built against the openai SDK’s Files and Batches resources, you can route those workloads through BatchRouter by changing two things: the base URL and the API key. No request reshaping required.

Behind that familiar surface, BatchRouter is a routing and aggregation layer, not a passthrough proxy to OpenAI. Your batch is quoted, funding-gated, and dispatched across BatchRouter’s provider lanes using the same pipeline as the native /v1/batches surface — and the responses you download are projected back into OpenAI’s shape so your client doesn’t notice the difference.

Use the compatible surface when you want a drop-in migration: minimal code changes to send existing OpenAI Batch jobs through BatchRouter.

Use the native /v1/batches surface when you want BatchRouter’s full feature set — free pre-submission quotes, sla_tier and routing_mode selection, privacy_tier controls, and routing/billing receipts. The OpenAI surface deliberately keeps the OpenAI request shape, so those BatchRouter-specific knobs aren’t expressed in the request body.

Point your OpenAI client’s base_url at:

https://api.batchrouter.com/v1/openai/v1

For the test environment, use https://test.api.batchrouter.com/v1/openai/v1.

Authenticate with a BatchRouter API key (prefixed br_live_) as the bearer token — exactly where the OpenAI SDK already puts OPENAI_API_KEY. Create a key in the dashboard (POST /v1/auth/account/api-keys) or via POST /v1/auth/agent-register. Keys are shown once.

from openai import OpenAI
client = OpenAI(
base_url="https://api.batchrouter.com/v1/openai/v1",
api_key="br_live_...", # your BatchRouter key, not an OpenAI key
)

These are the operations exposed under the OpenAI-compatible surface. They mirror the openai SDK’s client.files.* and client.batches.* methods.

Method & pathOpenAI SDK equivalent
POST /v1/openai/v1/filesclient.files.create({ file, purpose: "batch" })
GET /v1/openai/v1/files/{fileId}client.files.retrieve(id)
GET /v1/openai/v1/files/{fileId}/contentclient.files.content(id)
POST /v1/openai/v1/batchesclient.batches.create({ input_file_id, endpoint, completion_window })
GET /v1/openai/v1/batchesclient.batches.list()
GET /v1/openai/v1/batches/{batchId}client.batches.retrieve(id)
POST /v1/openai/v1/batches/{batchId}/cancelclient.batches.cancel(id)

For the full request/response field reference, see the interactive API reference or the raw spec.

The flow matches OpenAI’s Batch API exactly: upload a JSONL input file, create a batch from it, poll, then download the output file.

  1. Upload the input file. Send your OpenAI-Batch JSONL with purpose: "batch" to POST /v1/openai/v1/files (multipart/form-data). Each JSONL row is { custom_id, method, url, body }. BatchRouter converts each row into a native item — url selects the operation, body.model selects the model, and the rest of body becomes the item input. Invalid rows are rejected with an OpenAI-shaped error. You get back an OpenAI File object; keep its id.

  2. Create the batch. POST /v1/openai/v1/batches with { input_file_id, endpoint, completion_window }. The endpoint is the OpenAI endpoint your rows target — /v1/chat/completions, /v1/responses, or /v1/embeddings. Chat-completions and responses route to BatchRouter’s responses operation; embeddings route to embeddings. completion_window is "24h". The call is quote-free to you — BatchRouter quotes and funding-gates internally using the same pricing and billing controls as the native pipeline. You get back a bare OpenAI Batch object.

  3. Poll the batch. GET /v1/openai/v1/batches/{batchId} until status is terminal: completed, failed, expired, or cancelled. (In-flight statuses are OpenAI’s validatingin_progressfinalizing; cancelling is the in-progress cancel state.)

  4. Download the results. When completed, read output_file_id (and error_file_id for failures) from the Batch object and fetch the bytes with GET /v1/openai/v1/files/{fileId}/content. Result rows are projected to OpenAI’s per-line shape { id, custom_id, response: { status_code, request_id, body }, error }, keyed by your custom_id.

The OpenAI SDK handles the multipart upload for you.

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

A JSONL row looks like the OpenAI Batch format — for example:

{"custom_id":"item-1","method":"POST","url":"/v1/responses","body":{"model":"gpt-4o-mini","input":{"messages":[{"role":"user","content":"Summarize: BatchRouter routes batch-AI workloads across providers."}]}}}
import time
batch = client.batches.create(
input_file_id=input_file.id,
endpoint="/v1/responses",
completion_window="24h",
)
while batch.status not in ("completed", "failed", "expired", "cancelled"):
time.sleep(30)
batch = client.batches.retrieve(batch.id)
if batch.status == "completed":
output = client.files.content(batch.output_file_id)
print(output.text) # OpenAI-shaped JSONL, one object per line

Because BatchRouter routes across many providers, the per-line response.body you download is, by default, BatchRouter’s normalized cross-provider output. If you need each provider’s raw API response where it was captured, request raw:

  • Per batch — pass body_format (normalized | raw, default normalized) when creating the batch via POST /v1/openai/v1/batches.
  • Per download — pass ?body_format=raw on GET /v1/openai/v1/files/{fileId}/content to override for that one download.

The effective format is echoed in the x-batchrouter-body-format response header.

  • Errors are returned in OpenAI’s envelope. On insufficient funds, batch creation returns an OpenAI-shaped { "error": { "type": "insufficient_quota" } } — top up in the dashboard.
  • Output and error files are subject to BatchRouter’s retention. A file whose bytes have been reclaimed reports status: "expired" in its metadata and returns 410 from /content (distinct from a 404 for a file that never existed). See credits and billing and the API reference for retention details.

Once you’re routing through BatchRouter, the native /v1/batches API unlocks capabilities the OpenAI shape can’t express:

  • Free quotes before you commit (POST /v1/quotes/model, POST /v1/quotes/workflow).
  • sla_tierstandard (24h), flex, or priority.
  • routing_modecheapest (default), sla_aware, public_only, edge_only, hybrid, or privacy_constrained.
  • privacy_tierstandard, confidential, or restricted.
  • Receipts — routing and billing receipts (GET /v1/batches/{batchId}/billing-receipt).

These two surfaces operate on the same batches, so you can adopt the native API incrementally — keep the OpenAI client for existing jobs and reach for the native endpoints where you need more control.