Two powerful SDK features: Pydantic-based structured outputs for guaranteed JSON schema compliance, and the Batch API for high-volume processing at 50% lower cost.
Batch API Workflow
Batch API limits: max 50,000 requests per batch, 200 MB input file, 24-hour turnaround. Output files auto-delete after 30 days.
Schema-enforced beats prompt-suggested
The pre-structured-outputs era was: 'Reply with valid JSON matching this shape: ...' followed by defensive parsing and retries when the model returned commentary, code fences, or trailing prose. Schema enforcement removes that whole class of bug — the API rejects non-conforming output server-side and re-prompts the model. Your code receives a typed Pydantic instance.
When Batch API is right
Batch API is the answer to 'I have 50K prompts to run, none of them are interactive, and I want to spend half as much.' Submit a JSONL file, poll until done within 24h, download the results JSONL. Catastrophically wrong for chat (latency unbounded); perfect for evals, dataset generation, content moderation backfills.
Code
Structured output via response_format with Pydantic·python
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class ResearchSummary(BaseModel):
title: str
key_findings: list[str]
conclusion: str
confidence_score: float
# Using responses.parse() for structured output
response = client.responses.parse(
model="gpt-5.4",
input="Summarize the key findings of quantum computing research.",
text_format=ResearchSummary,
)
summary: ResearchSummary = response.output_parsed
print(summary.title)
print(summary.key_findings)
Batch API: submit + poll + retrieve·python
import json
from openai import OpenAI
client = OpenAI()
# 1. Create JSONL batch file
requests = [
{"custom_id": f"request-{i}", "method": "POST", "url": "/v1/responses",
"body": {"model": "gpt-5.4", "input": f"Summarize: {text}"}}
for i, text in enumerate(texts)
]
with open("batch_input.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req) + "\\n")
# 2. Upload → Create batch → Poll → Retrieve
with open("batch_input.jsonl", "rb") as f:
batch_file = client.files.create(file=f, purpose="batch")
batch = client.batches.create(
input_file_id=batch_file.id,
endpoint="/v1/responses",
completion_window="24h",
)
Define a Pydantic model RecipeCard{title:str, prep_min:int, ingredients:list[str], steps:list[str]}. Get the model to return 5 recipes — once via response_format, once by hand-rolled prompt + json.loads. Compare the failure rates.
Progress
Progress is local-only — sign in to sync across devices.