Structuring a batch job: the JSONL request file
A JSONL file is the entire interface to a batch run. A few habits in how you build it make jobs cheaper, easier to join back, and far less painful to debug.
One request per line
A batch input is a JSONL (JSON Lines) file: each line is a complete, self-contained JSON object describing one request. A line carries an identifier you choose, the endpoint, and the request body — the same chat-completion payload you would send interactively.
{"custom_id":"sku-1001","method":"POST","url":"/v1/chat/completions","body":{"model":"otium-medium","messages":[{"role":"system","content":"Write a concise product description."},{"role":"user","content":"Stainless water bottle, 32oz, vacuum insulated."}]}}
{"custom_id":"sku-1002","method":"POST","url":"/v1/chat/completions","body":{"model":"otium-medium","messages":[{"role":"system","content":"Write a concise product description."},{"role":"user","content":"Merino wool socks, ankle height, grey."}]}}The output is the same shape: one result line per input line, each carrying the matching custom_id so you can join results back to your records regardless of the order they complete in.
Choose stable custom IDs
The custom_id is your join key. Make it something stable and meaningful from your own system — a primary key, a SKU, a record UUID — not a row number. Stable IDs make results trivial to merge back, make partial reruns safe (resubmit just the failed IDs), and make debugging a specific item painless. Avoid reusing an ID for two different items in the same job.
Design prompts for caching and consistency
Because every line shares structure, small prompt choices multiply across the whole job:
- Put the stable part first. Keep the system prompt, instructions, and examples identical across lines and at the front, so prompt caching can reuse them and the per-item cost drops.
- Keep the variable part minimal. Only the item-specific text should change line to line. Trim anything that rides along unnecessarily — you pay for it on every line.
- Constrain the output. Ask for a specific format (and a token cap) so outputs are short, parseable, and predictable. Output tokens are the expensive ones.
- Be consistent. Uniform prompts produce uniform, easier-to-validate results and batch more efficiently.
Keeping large jobs manageable
For very large runs, split work into reasonably-sized files rather than one giant file — it makes uploads, retries, and progress tracking easier, and lets you start consuming results sooner. Validate the JSONL before submitting (each line parseable, required fields present, IDs unique). And keep a manifest of which IDs you submitted so you can confirm completeness when results come back and resubmit any stragglers.
When a flat file stops being the right shape
Everything above assumes each line is independent — which is what makes a JSONL file such a good interface. Lines can be sharded, retried, and reordered freely because none of them needs anything from another.
That assumption breaks the moment your work has stages: extract, then classify using what you extracted, then summarise. You can still express it as batch files, but you have to submit one file per stage and wait for each to finish before building the next. Every stage pays the full completion window, so a seven-stage pipeline on a seven-day window is seven weeks — and the platform never learns that the stages were related, which is exactly the information that would let it schedule them well.
If that describes your work, the file is no longer the unit worth optimizing and a separate guide picks the thread up.
Frequently asked questions
What should I use for custom_id?
A stable, meaningful identifier from your own system — a primary key, SKU, or record UUID — not a row number. It is your join key for merging results back, makes partial reruns safe, and keeps upserts idempotent.
How do I make a batch job cheaper through its structure?
Keep the stable part of the prompt (system instructions, examples) identical and at the front so prompt caching reuses it, minimize the variable per-item text, and constrain outputs to a short, specific format since output tokens cost the most.
Related guides
OpenAI-compatible batch surfaces make switching providers a base-URL change, not a rewrite. How the file-and-batch flow works, and what to check when you move.
Prompt caching reuses the computed state of a repeated prefix so you stop paying full price for it. How it works, why batch benefits most, and how it is billed.
Tokens are what AI models read and write, and what you are billed on. What a token is, why output costs more than input, and how to estimate a job up front.
If your AI work is a pipeline, submitting one request at a time discards the structure that would let it be scheduled well. What the research says that costs.