Structured outputs
Structured outputs make a model reply with JSON instead of prose — either any valid JSON object, or JSON that follows a schema you supply. It is the reliable way to extract fields, classify text, or feed a reply straight into code.
Use a model whose capabilities.json_mode is true in the model list.
JSON schema
const completion = await client.chat.completions.create({
model: "gpt-5.5",
messages: [
{ role: "system", content: "Extract the event details." },
{ role: "user", content: "Team offsite on March 3rd at the Lisbon office, 10am." },
],
response_format: {
type: "json_schema",
json_schema: {
name: "event",
strict: true,
schema: {
type: "object",
properties: {
title: { type: "string" },
date: { type: "string" },
location: { type: "string" },
},
required: ["title", "date", "location"],
additionalProperties: false,
},
},
},
});
const event = JSON.parse(completion.choices[0].message.content);
With strict: true, models that support strict schemas only produce output that validates. Strict mode supports a subset of JSON Schema: set additionalProperties: false on every object and list every property in required (use a union with null for optional values).
JSON mode
For valid JSON without a fixed schema, use json_object. Say in the prompt that you want JSON and describe the shape, since the model has nothing else to go on:
{
"model": "gemini-3.6-flash",
"response_format": { "type": "json_object" },
"messages": [
{ "role": "user", "content": "List three primary colours as JSON: {\"colours\": [...]}" }
]
}
On the Responses API
The schema goes under text.format:
{
"model": "gpt-5.5",
"input": "Team offsite on March 3rd at the Lisbon office, 10am.",
"text": {
"format": {
"type": "json_schema",
"name": "event",
"strict": true,
"schema": { "type": "object", "properties": { "title": { "type": "string" } }, "required": ["title"], "additionalProperties": false }
}
}
}
The SDK helpers for structured outputs (client.chat.completions.parse with Zod or Pydantic, client.responses.parse) send the same fields and work unchanged.
Things to check
- A model that does not support a schema returns a
400rather than quietly ignoring it. Pick another model or fall back to JSON mode. - A reply cut short by
max_tokens(finish_reason: "length") is incomplete JSON. Leave room in the limit. - A model can still decline a request; check for a refusal before parsing.
- Validate the parsed object anyway. A schema guarantees shape, not truth.
- On the Messages endpoint, which has no
response_format, the usual pattern is a single tool whoseinput_schemais your schema, withtool_choiceforcing it.