Structured Outputs
Ask a model to describe a customer's order and you might get a paragraph, a bulleted list, or a sentence with the fields in a different order than last time. Ask the same thing with structured output turned on, and you get the same shape every time — say, {"order_id": "...", "status": "...", "total": 12.50} — because the response is forced to match a schema you defined, not just asked nicely to follow one.
Structured output means the model's response is mechanically constrained to a shape you specify, so code can parse it directly instead of a person — or a fragile pattern-matcher — trying to make sense of freeform text.
How it's actually enforced
Requesting a format in the prompt — "please respond only in JSON" — is a request, not a guarantee. The model can still drift, especially over a longer response, because nothing stops it from writing something that almost matches your shape.
Real structured output works differently: your schema gets compiled into rules about what can legally come next at each step of generation. As the model writes its response one token at a time — a small chunk of text, often close to a whole word or part of one — anything that would break the schema is blocked before it's even considered, not caught and fixed afterward. The model isn't being polite about the format. It mechanically cannot produce something outside it.
What this actually buys you
The hallucination page makes this exact point about closed sets of labels: a prompt that just lists five allowed categories doesn't stop the model from inventing a sixth — it only makes a sixth less likely. Real schema enforcement is what turns "less likely" into "impossible." The same mechanism is what a tool call's arguments rely on — when a model calls a tool, the arguments it fills in are themselves structured output, constrained to match the tool's declared parameters, which is why a tool call can't hand your code an argument in the wrong shape.
What it doesn't buy you
A valid shape is not the same as correct content. Structured output can guarantee the response is exactly {"category": "refund", "confidence": 0.0-1.0} with a category from your allowed list — it cannot guarantee "refund" is the right category for this particular case. Enforcement operates on the shape and the allowed values, not on whether the specific answer is true. Treat it as a fix for unreliable formatting, not a fix for wrong answers.
Extraction, classification, and tool arguments
Pulling fields out of unstructured text. A receipt, an email, or a support ticket goes in; a fixed set of fields — vendor, amount, date — comes out in a shape your database can insert directly, instead of a paragraph you'd have to parse with regular expressions.
Classification with a closed label set. Sorting a support ticket into one of five categories, where a sixth, invented category would break whatever code reads the result next.
Tool call arguments. Every time a model calls a tool, it's producing structured output — the arguments have to match the tool's schema or your code has nothing safe to execute.
When you don't need it
A plain chat reply meant to be read by a person doesn't need a schema — forcing conversational text into a rigid shape adds constraint for no benefit. Reach for structured output specifically when something downstream — your code, a database, another system — has to parse the response without a human reading it first.
In this guide
FAQ
If a model's response is valid according to my schema, does that mean the content is correct?
No. Schema enforcement guarantees the shape and, for a closed set of values, that only allowed values appear — it says nothing about whether the specific value chosen is actually right for the input. A classifier can return a perfectly valid, schema-conforming answer that's still the wrong category.
Isn't asking the model to "only respond in JSON" good enough?
It's a request, not an enforced constraint — the model is still free to drift, and often does over a longer or more complex response. Genuine structured output blocks invalid tokens during generation itself, so the response can't leave the schema in the first place, rather than hoping it stays inside one because you asked politely.