OpenAI Structured Outputs: The Complete Guide to json_schema, strict Mode, and Common Errors
The full path to reliably parseable JSON: the Responses API text.format syntax, strict mode, schema design tips, and troubleshooting for high-frequency errors like passing schema without name.
How to
Define the JSON Schema
Write the desired output shape as a JSON Schema: top-level type: object, fields under properties, required fields in required, and enum for closed sets. The more fields, the more the descriptions matter.
Wrap it in text.format with a name
Add text={"format": {"type": "json_schema", "name": a-config-name, "strict": true, "schema": your-schema}} to the request. name is required -- passing only the schema errors out.
Send the request with input
The Responses API message field is input (an array), not messages. Put system instructions and user input both in input.
Parse output_text defensively
Take resp.output_text and json.loads it; the shape is guaranteed by the schema, but keep one business-semantic check (enum validity, date ranges) in application code.
Structured outputs are OpenAI API's constrained-decoding capability: you provide a JSON Schema in the request, and the model is constrained to emit JSON conforming to that schema -- no regex extraction or format repair, straight into json.loads / JSON.parse in your application code. This tutorial covers the complete Responses API syntax, what strict mode actually guarantees, and troubleshooting for the most common errors (starting with passing the schema without a name).
Note: the code in this article was verified line-by-line against the official Structured Outputs guide (docs-checked version); the
lastTestedAtfield marks the verification date. Run it in a test environment before production use.
1. Quick Start: A Minimal Working Example
In the Responses API, structured outputs hang off the text parameter. Inside format you have the trio type, name, and schema, plus the strict switch.
from openai import OpenAI
client = OpenAI()
schema = {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {"type": "string"},
"description": "Solution steps in order",
},
"answer": {"type": "string", "description": "The final answer"},
},
"required": ["steps", "answer"],
}
resp = client.responses.create(
model="gpt-5.6-luna",
input=[
{"role": "system", "content": "You are a math tutor. Follow the schema exactly."},
{"role": "user", "content": "A word problem: Amy has 3 apples and buys 5 more. How many?"},
],
text={
"format": {
"type": "json_schema",
"name": "math_response",
"strict": True,
"schema": schema,
}
},
)
data = json.loads(resp.output_text)
print(data["answer"], data["steps"])
Three places where mistakes are most likely: the message field is input, not messages; the schema is wrapped inside text.format, not at the top level; and name is required. The official guide's example is exactly this shape: text={"format": {"type": "json_schema", "name": "math_response", "schema": ...}}.
2. The Node.js Equivalent
import OpenAI from "openai";
const client = new OpenAI();
const schema = {
type: "object",
properties: {
sentiment: { type: "string", enum: ["positive", "neutral", "negative"] },
score: { type: "number" },
},
required: ["sentiment", "score"],
};
const resp = await client.responses.create({
model: "gpt-5.6-luna",
input: [
{ role: "system", content: "Classify user reviews. Follow the schema exactly." },
{ role: "user", content: "Battery life is great, but the fit is mediocre." },
],
text: {
format: {
type: "json_schema",
name: "sentiment_response",
strict: true,
schema,
},
},
});
const data = JSON.parse(resp.output_text);
console.log(data.sentiment, data.score);
Enums are the most useful constraint in structured outputs: for classification tasks, write the label space into the schema and the model cannot output values outside it.
3. strict Mode: Guaranteed Shape, Not Semantics
strict: true means constrained decoding -- the output is generated to follow the schema exactly, with field presence and types guaranteed. Omitted or false, the model "tries" to comply and can drift out of valid JSON on long text or deep nesting.
Separate the two layers of guarantee:
- Shape guarantee (what strict gives): fields exist, types match, enum values stay in the set.
- Semantic correctness (what strict does not give): whether
answeris the right answer, whethersentimentmatches human judgment -- that depends on your prompts and the model.
So the production setup is double insurance: strict ensures json.loads never throws, and the application keeps one semantic validation pass (enum validity, numeric ranges, date formats) with retry or fallback on failure.
data = json.loads(resp.output_text)
# Shape is guaranteed by strict; this checks the semantics strict does not
assert data["sentiment"] in {"positive", "neutral", "negative"}
if not 0 <= data["score"] <= 1:
raise ValueError("score out of range -- retry or fall back")
4. Schema Design Tips
- Top level is always an object: hang all fields off the top-level
propertiesinstead of a bare array -- parsing and validation stay simple. - Fill in required: put every field you expect into
required; under strict mode, explicitrequiredbeats hoping fields show up. - Prefer enums over free text: any closed set (positive/neutral/negative, low/mid/high) should be an
enum, constraining the model to your label space. - Descriptions are for the model: each field's
descriptionis where the model reads semantic constraints -- put format requirements (like "YYYY-MM-DD") right there. - Do complex validation in the app: regexes and cross-field consistency checks do not belong in the schema; validate after the response in application code.
5. Common Errors and Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Error when passing only the schema | format is missing name | Add the name field -- it is required |
Unknown parameter messages | Used the Chat Completions field | The Responses API uses input (an array) |
| Output is occasionally invalid JSON | strict: true not set | Turn strict on; do not rely on "best effort" |
| Enum values outside the set | No enum in the schema, or strict off | Add enum to the field and enable strict |
| Valid JSON but wrong values | Shape is fine, semantics drifted | Strengthen descriptions and the system prompt; add business validation |
| Long nested output gets cut off | Output token limit reached | Simplify the schema, split the task, or raise the output limit |
The "schema without name" failure is the most common -- the error message does not always point at name, so people waste time hunting through schema syntax. Remember the trio: type, name, schema, plus the strict switch.
6. Structured Outputs or Function Calling?
They share the JSON Schema description language but solve different problems:
| Dimension | Function calling | Structured outputs |
|---|---|---|
| What it solves | The model decides which tool to call and generates arguments | Constrains the JSON shape of the final answer |
| Who initiates | The model emits a function_call; your code executes | You initiate the request and get the result back |
| Typical use | Query a database, call internal APIs, perform actions | Classification, extraction, tagging, fixed-format content |
| Flow | function_call -> execute -> function_call_output | The response directly contains schema-conformant text |
One line: use function calling when the model should "do things," structured outputs when it should "submit homework in your format." Both ride on the Responses API's tool and format mechanisms with a consistent declaration style -- learn one and the other is nearly free.
Frequently Asked Questions
1. How do structured outputs relate to function calling?
They share the same schema machinery but solve different problems: function calling lets the model decide which tool to call and generate arguments; structured outputs constrain the JSON shape of the final answer. Use structured outputs when downstream code consumes the answer; use function calling when the model should trigger capabilities on your side. A single request can use just one of them.
2. Why does it error when I only pass the schema?
In the Responses API the schema must be wrapped in text.format, and format must include a name field -- name identifies this output configuration. Passing only {"type": "json_schema", "schema": ...} without name errors out. See the first example for the complete shape.
3. What is the difference between strict: true and omitting strict?
strict: true is constrained decoding: the output follows the schema exactly, with field presence and types guaranteed. Without it the model "tries" to comply and can drift on long or deeply nested content. Turn strict on everywhere the output goes straight into json.loads.
4. Can I use regexes or custom formats in the schema?
The well-supported subset is JSON Schema's core: object, array, string, number, enum, and required. Exotic features have weaker compatibility -- express constraints with basic types plus enum, and do complex validation in your application.
5. The JSON is valid but the field values are wrong -- what now?
Structured outputs guarantee shape, not semantics. Put semantic constraints into field descriptions and the system prompt (annotate enum values, give date format examples), and keep a business-level validation pass in the application -- valid shape plus semantic checks is the production-grade setup.
6. Does the Chat Completions response_format still work?
Legacy projects can keep using response_format, but Chat Completions is in legacy status; new projects should standardize on the Responses API (the input field plus text.format). All examples in this article use the Responses API.
Next Steps
- Need the model to take actions, not just submit homework? Read Function Calling with the OpenAI API: A Complete Guide to Tool Use in the Responses API.
- New to the OpenAI API? Read OpenAI API Beginner: Your First GPT-5.6 Call Explained.
- Want the bigger Responses API picture? Read Responses API advanced: structured outputs, streaming SSE, Batch API, prompt caching.
Key points
- In the Responses API pass text={"format": {"type": "json_schema", "name": ..., "strict": true, "schema": ...}} -- the schema is wrapped in format and name is required
- Passing only the schema without name errors out immediately -- the single most common failure
- strict: true means the output follows the schema exactly; omitting it (or false) means the model only 'tries' to comply
- The messages field is input (an array), not messages -- new projects standardize on the Responses API
- Function calling and structured outputs are different things: the former decides which tool to call, the latter constrains the shape of the answer
- Parse defensively anyway: the schema constrains shape, not business semantics
Frequently asked questions
Official references
Related articles
Responses API advanced: structured outputs, streaming SSE, Batch API, prompt caching
Responses API advanced usage: JSON Schema strict mode, streaming SSE parsing, Batch API offline cost-cut, prompt caching three-tier cache, cost optimization case studies. From 'it calls' to 'production-grade'.
Read articleOpenAI API Error Handling and Retry: 401/429/5xx Patterns
Production-ready error handling for the OpenAI API: 401/429/500/503/timeout. Exponential backoff + jitter, error budgets, upstream protection, streaming-mode specifics.
Read articleFunction Calling with the OpenAI API: A Complete Guide to Tool Use in the Responses API
Function calling is how GPT-5.6 calls your code. This guide walks the full Responses API flow: declaring tools, parsing function_call, returning results, and chaining multi-turn tool calls.
Read articleSubscribe to GPTMap Weekly
One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.