If you need to process thousands of GPT-5 completions without real-time latency constraints, the GPT-5 batch API gateway access pattern gives you async throughput and provider resilience that direct OpenAI calls can’t match on their own. This guide walks through standing up batch jobs against an OpenAI-compatible gateway, with the tradeoffs you should weigh before routing production traffic.
1. What the batch API actually buys you
OpenAI’s batch endpoint accepts a file of requests and processes them within a 24-hour window at a 50% token discount. Direct integration means you manage file uploads, job polling, and per-organization rate limits yourself. A gateway sits in front of that flow and normalizes it behind a single API surface, often adding fallback and unified metering.
The core primitives are unchanged:
- Upload a JSONL file with
purpose="batch". - Create a batch referencing that file and an
endpoint. - Poll
statusuntilcompleted. - Download the output file before it expires (24h after completion).
2. Point an OpenAI-compatible client at the gateway
Most gateways expose an OpenAI-compatible REST surface so you can reuse the official SDK. You only swap base_url and api_key.
from openai import OpenAI
client = OpenAI(
base_url="https://gateway.example.com/v1", # gateway endpoint
api_key="your-gateway-key",
)
# Verify model availability
models = client.models.list()
print([m.id for m in models if "gpt-5" in m.id])
A gateway like n4n.ai exposes one OpenAI-compatible endpoint that addresses 240+ models and will automatically fall back when a provider is rate-limited or degraded, so your batch submission doesn’t sit in a dead queue. That matters for batch because a stuck job misses the 24h SLA and you eat the redo cost.
3. Build a correct JSONL input
Each line is a self-contained request. The url must match the batch endpoint you declare later. For chat completions:
{"custom_id": "job-001", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-5", "messages": [{"role": "user", "content": "Extract key clauses from: {{contract_text}}"}], "max_tokens": 512, "temperature": 0}}
{"custom_id": "job-002", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-5", "messages": [{"role": "user", "content": "Summarize: {{report_text}}"}], "max_tokens": 256, "temperature": 0}}
Common pitfalls:
- Model name drift: gateway may map
gpt-5to a specific provider snapshot. Pin the exact string the gateway returns frommodels.list(). - Missing
max_tokens: batch jobs fail silently per-line if a param is out of range. - Oversized lines: keep each request body under 100KB; large prompts belong in prompt-cache, not inline repetition.
4. Upload and submit the batch
Upload the file, then create the batch. The completion_window is always "24h" for OpenAI-compatible batch.
import time
with open("batch_requests.jsonl", "rb") as f:
file_obj = client.files.create(file=f, purpose="batch")
batch = client.batches.create(
input_file_id=file_obj.id,
endpoint="/v1/chat/completions",
completion_window="24h",
)
print(f"Submitted batch {batch.id}, status {batch.status}")
Tradeoff: the gateway adds one network hop on submission, but submission is a tiny fraction of total batch latency. Don’t optimize for it.
5. Route explicitly when you need provider pinning
Some gateways honor client routing directives via headers or body extensions. If you need GPT-5 from a specific provider (e.g., for compliance), set the header your gateway documents. Forwarding provider cache-control hints is also critical—if your prompt prefix is stable across the batch, the gateway should pass cache_control so the provider caches it.
# Example with a routing header (check your gateway's spec)
batch = client.batches.create(
input_file_id=file_obj.id,
endpoint="/v1/chat/completions",
completion_window="24h",
extra_headers={"x-router-prefer": "openai"},
)
If you skip this, the gateway may load-balance across providers. That’s fine for throughput, but not if you need deterministic provider behavior for eval reproducibility.
6. Poll without burning cycles
Batch completion is async. Poll on a coarse interval; don’t hit the API every second.
while True:
status = client.batches.retrieve(batch.id)
if status.status in ("completed", "failed", "expired"):
break
time.sleep(120) # 2-minute polling is plenty
if status.status != "completed":
raise RuntimeError(f"Batch ended with {status.status}")
When a provider is degraded mid-job, a gateway with automatic fallback—n4n.ai does this—will shift remaining requests to a healthy route without you re-submitting. Direct OpenAI has no such escape hatch; you wait or cancel.
7. Retrieve and parse results
Output is a JSONL file keyed by custom_id. Download it promptly; OpenAI expires completed batch files after 24h, and gateways rarely extend that.
result = client.files.content(status.output_file_id)
with open("batch_output.jsonl", "wb") as f:
f.write(result.read())
# Parse
import json
with open("batch_output.jsonl") as f:
for line in f:
rec = json.loads(line)
if rec["error"]:
print(f"{rec['custom_id']} failed: {rec['error']}")
else:
print(rec["custom_id"], rec["response"]["body"]["choices"][0]["message"]["content"][:80])
Per-line errors don’t fail the whole batch. You must scan for them.
8. Cost tracking and per-token metering
Batch discounts apply at the provider, but reconciling which team burned tokens is messy with direct invoices. A gateway that provides per-token usage metering lets you attribute cost by API key or tag without post-processing provider bills.
The response bodies include usage objects:
{"custom_id": "job-001", "response": {"body": {"usage": {"prompt_tokens": 120, "completion_tokens": 45, "total_tokens": 165}}}}
Aggregate those locally or rely on the gateway’s metering endpoint. Tradeoff: gateway markup (if any) is visible as a slight per-token premium over direct, but you buy resilience and observability.
9. Gateway vs OpenAI direct: the real tradeoffs
| Dimension | Direct OpenAI batch | Via gateway |
|---|---|---|
| Setup | Native SDK, no extra hop | Change base_url, learn routing headers |
| Resilience | No fallback; org limits hard-stop | Fallback on rate-limit/degrade |
| Model coverage | Only OpenAI models | 240+ models behind same API |
| Cost clarity | Provider invoice only | Per-token metering, unified |
| Data path | Direct to OpenAI | Extra proxy; review compliance |
If you’re all-in on OpenAI and never hit rate limits, direct is simpler. If you run batches at scale and can’t afford a 24h job to stall, GPT-5 batch API gateway access is the safer architecture.
10. Actionable checklist
- Confirm gateway model string for
gpt-5viaclient.models.list(). - Write JSONL with explicit
max_tokensand stablecustom_id. - Upload with
purpose="batch". - Create batch with
endpoint="/v1/chat/completions"andcompletion_window="24h". - Set routing header only if provider pinning is required.
- Poll every 2 minutes; handle
failed/expiredstates. - Download output within 24h; scan every line for
error. - Sum
usage.total_tokensper tag for cost allocation.
Follow that order and you’ll have a reproducible batch pipeline that survives provider hiccups without rewriting your client.