Skip to content

Files & large batches

Most batches submit their work inline as an array of items. But two cases call for uploading a file first: a large JSONL request set that you’d rather not inline, and binary inputs (images, PDFs, audio) that a multimodal model needs to read. Both flow through POST /v1/files, which stores the bytes and returns a file_id you reference from your batch.

This guide covers the native file-upload endpoint, when to use a file versus inline items, and how to attach an image or document to an item.

Use a file instead of inline items when either is true:

  • The request set is large. Inlining thousands of items in one JSON body is awkward and memory-heavy on both ends. Upload the request set as a JSONL file and pass its id as the batch’s input_file_id. See the JSONL format guide for the row shape.
  • An item needs a binary input. Images, PDFs, Office documents, audio, or video can’t live inline in JSON. Upload each one, then reference its file_id inside an item’s content blocks.

POST /v1/files takes a raw binary body — not multipart form data. You describe the file through headers:

HeaderRequiredPurpose
Content-TypeYesThe actual MIME type of the body (e.g. image/png, application/pdf, application/json).
Content-LengthYesExact size in bytes, so the upload limit is enforced before the body is stored.
X-BatchRouter-FilenameNoOriginal filename. URL-encode it if it contains spaces or non-ASCII characters.
X-BatchRouter-PurposeNoUpload purpose. Defaults to model_input.
Terminal window
curl -X POST https://api.batchrouter.com/v1/files \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Content-Type: image/png" \
-H "Content-Length: $(wc -c < ./chart.png)" \
-H "X-BatchRouter-Filename: chart.png" \
-H "X-BatchRouter-Purpose: model_input" \
--data-binary @./chart.png

A successful upload returns 201 with the file_id and file metadata:

{
"file_id": "file_9f3c2a...",
"file": {
"id": "file_9f3c2a...",
"kind": "input_attachment",
"content_type": "image/png",
"size_bytes": 48210,
"filename": "chart.png",
"media_kind": "image",
"created_at": "2026-06-18T10:21:00Z"
},
"usage": {
"image_block": { "type": "input_image", "file_id": "file_9f3c2a..." }
}
}

The usage block is a ready-to-paste content block for the file’s media kind — image_block for images, file_block for documents — so you can drop it straight into an item.

Supported categories include image/*, audio/*, video/*, text/*, PDF, Word, PowerPoint, Excel, RTF, JSON, and XML. The endpoint returns 413 if the body exceeds the upload limit, 415 for an unsupported content type, and 411 if Content-Length is missing.

Reference an image or binary input in an item

Section titled “Reference an image or binary input in an item”

For multimodal inputs, reference the file_id inside an item’s message content as an input_image (for images) or input_file (for documents and other binaries) block, alongside any text:

{
"customer_item_id": "item-1",
"operation": "responses",
"model": "gpt-4o-mini",
"input": {
"messages": [
{
"role": "user",
"content": [
{ "type": "input_text", "text": "Describe the trend in this chart." },
{ "type": "input_image", "file_id": "file_9f3c2a..." }
]
}
]
}
}

For a PDF or document, use an input_file block (which may carry an optional filename):

{ "type": "input_file", "file_id": "file_9f3c2a...", "filename": "report.pdf" }

These content blocks work in both inline items and JSONL rows uploaded as an input_file_id.

When the requests themselves are numerous, upload them as a JSONL file (one item per line) and pass the returned id as the batch’s input_file_id instead of inline items.

  1. Build the JSONL. One batch item per line, in the JSONL format. Mixed-model JSONL is supported — BatchRouter keeps one customer batch and splits execution into model-specific lanes.

  2. Upload it to POST /v1/files with Content-Type: application/json (the same upload call shown above), and keep the returned file_id.

  3. Create the batch with input_file_id set to that id — no items field:

    Terminal window
    curl -X POST https://api.batchrouter.com/v1/batches \
    -H "Authorization: Bearer $BATCHROUTER_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
    "input_file_id": "file_9f3c2a...",
    "sla_tier": "standard",
    "routing_mode": "cheapest"
    }'

From here the flow is identical to inline submission: the batch returns 202 with a bat_... id, and you poll and fetch results as usual. See Submit a batch for the full lifecycle.