Files & batches

POST /v1/files and POST /v1/batches — a JSONL file of requests, run in the background, results as files.

POSThttps://iranrouter.com/v1/files

Upload a file (multipart: file and purpose). Two uses: a batch input with purpose=batch, and referring to a file inside a conversation by file_id (below). The type is read from the CONTENT, not the extension: documents (PDF, Word, Excel, PowerPoint), images, audio and text (JSON, JSONL, CSV, Markdown) are accepted; an executable or an archive is refused with 415. A path in the filename (invoices/2026/march.pdf) or a `folder` field becomes the file's folder — a label to group and filter by (GET /v1/files?folder=…), not a real directory. The file is kept until you delete it (no expiry; up to 100 MB each and 10 GB per account by default (your account may be granted more) — past that, 413 with code file_quota_reached) and managed with GET /v1/files, GET /v1/files/{id}, GET /v1/files/{id}/content and DELETE /v1/files/{id} — OpenAI's shapes.

Shell
curl https://iranrouter.com/v1/files \
  -H "Authorization: Bearer ir-..." \
  -F purpose=batch \
  -F file=@requests.jsonl
JSON
{ "id": "9c1e...-file-id", "object": "file", "bytes": 4210, "created_at": 1789000000, "filename": "requests.jsonl", "purpose": "batch", "status": "processed" }
POSThttps://iranrouter.com/v1/batches

A JSONL file of requests for one endpoint runs in the background, line by line, through the ordinary path — each line an ordinary request with the ordinary bill and limits of that key — and the results come back as files. No discount: a batch is a convenience, not a different price. completion_window is accepted but not a promise: the batch starts at once.

Text
{"custom_id": "q1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "سلام"}]}}
{"custom_id": "q2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "خداحافظ"}]}}
Shell
curl https://iranrouter.com/v1/batches \
  -H "Authorization: Bearer ir-..." \
  -H "Content-Type: application/json" \
  -d '{"input_file_id": "9c1e...-file-id", "endpoint": "/v1/chat/completions", "completion_window": "24h"}'
FieldNotes
endpoint/v1/chat/completions · /v1/responses · /v1/messages · /v1/embeddings · /v1/completions · /v1/moderations · /v1/rerank
statusvalidating → in_progress → finalizing → completed | failed | cancelling → cancelled
request_countstotal, completed, failed — updated every ten lines.
output_file_id / error_file_idSuccesses and failures, each line {id, custom_id, response:{status_code, request_id, body}, error}. Content from GET /v1/files/{id}/content.
POST /v1/batches/{id}/cancelStops after the lines in flight; the output so far is written.
  • Up to 1000 lines per batch; up to 5 open batches per user (past that, 429 batch_limit_reached).
  • Each line's url must equal the batch endpoint; stream is switched off; an invalid line goes to the error file and the rest continue.
  • A line refused with a 429 for rate is retried after retry-after, up to three times; a 402 for balance, or a 429 that persists, puts that line in the error file — the batch does not stop. A key revoked mid-run stops the batch, and the lines that did not run are listed in the error file.

For one request, the async twin →