Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/openai/openai-cookbook/llms.txt

Use this file to discover all available pages before exploring further.

Structured Outputs is a capability in the Chat Completions API that guarantees every model response conforms exactly to a JSON schema you define. Unlike JSON mode — which only ensures the model emits valid JSON — Structured Outputs enforces 100% schema adherence: every required field is present, every type matches, and no extraneous properties appear. This makes it possible to consume model output directly in typed application code without defensive parsing or error handling.

Why Structured Outputs matters

Before Structured Outputs, extracting structured data from model responses required prompt engineering (“respond only with JSON”), fragile regex parsing, or retry loops to handle malformed output. Even with JSON mode enabled, the model might omit optional fields, use wrong types, or include keys not in your schema. Structured Outputs eliminates this class of problem entirely. The constraint is enforced at the sampling layer — the model literally cannot produce a token sequence that violates your schema.

100% schema adherence

Every field, type, and constraint in your schema is enforced. No parsing surprises in production.

Pydantic integration

Pass a Pydantic model directly and get back a fully-typed Python object via .parsed.

Refusal handling

When the model declines to answer, the refusal is surfaced cleanly so you can handle it explicitly.

Function calling support

Use strict: true on function definitions to enforce schemas on tool call arguments too.

Using Pydantic models with .parse()

The simplest way to use Structured Outputs is with the client.beta.chat.completions.parse() method and a Pydantic model. The SDK automatically converts your model into a JSON schema, sends it to the API, and deserializes the response into a typed object.
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

completion = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    response_format=CalendarEvent,
)

event = completion.choices[0].message.parsed
print(event.name, event.date, event.participants)
# Science Fair  Friday  ['Alice', 'Bob']
The .parsed attribute contains a fully-typed CalendarEvent instance — no json.loads() required.

Nested models

Pydantic supports arbitrarily nested models, which the SDK converts into a nested JSON schema automatically.
from pydantic import BaseModel
from typing import Optional

class Step(BaseModel):
    explanation: str
    output: str

class MathSolution(BaseModel):
    steps: list[Step]
    final_answer: str

completion = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {
            "role": "system",
            "content": "You are a math tutor. Solve the problem step by step.",
        },
        {"role": "user", "content": "How do I solve 8x + 7 = -23?"},
    ],
    response_format=MathSolution,
)

solution = completion.choices[0].message.parsed
for i, step in enumerate(solution.steps, 1):
    print(f"Step {i}: {step.explanation}")
    print(f"  {step.output}")
print(f"Answer: {solution.final_answer}")
Pydantic’s Field() annotations — including description, ge, le, and pattern — are respected and included in the generated schema. Use them to give the model additional guidance about each field.

Using the JSON Schema approach

If you prefer not to use Pydantic, or need to define schemas dynamically at runtime, you can pass a raw JSON schema via the response_format parameter with type: "json_schema".
import json
from openai import OpenAI

client = OpenAI()

schema = {
    "type": "json_schema",
    "json_schema": {
        "name": "calendar_event",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "date": {"type": "string"},
                "participants": {
                    "type": "array",
                    "items": {"type": "string"}
                }
            },
            "required": ["name", "date", "participants"],
            "additionalProperties": False
        }
    }
}

completion = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    response_format=schema,
)

event = json.loads(completion.choices[0].message.content)
print(event)
additionalProperties: false and all fields listed under required are mandatory when strict: true is set. The API will reject schemas that do not satisfy these constraints.

Structured Outputs with function calling

You can also apply strict: true to function definitions to guarantee that the arguments the model passes to your function match the schema exactly.
tools = [
    {
        "type": "function",
        "function": {
            "name": "create_calendar_event",
            "description": "Create a new calendar event",
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "Event name"},
                    "date": {"type": "string", "description": "Event date in YYYY-MM-DD format"},
                    "participants": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "List of participant names"
                    }
                },
                "required": ["name", "date", "participants"],
                "additionalProperties": False
            }
        }
    }
]

completion = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "Schedule a team lunch for Alice, Bob, and Carol next Monday."}
    ],
    tools=tools,
)

tool_call = completion.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
print(args)

Handling refusals

When the model determines it cannot fulfill a request — due to safety policies or content concerns — it returns a refusal rather than structured output. Check for this before accessing .parsed.
completion = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "user", "content": "..."},
    ],
    response_format=CalendarEvent,
)

message = completion.choices[0].message

if message.refusal:
    print("Model declined to respond:", message.refusal)
else:
    event = message.parsed
    print(event)
Always check message.refusal before accessing message.parsed. Accessing .parsed on a refusal response will raise an exception.

Supported schema types and limitations

Structured Outputs supports a rich subset of JSON Schema, but not every feature:
  • Primitive types: string, number, integer, boolean, null
  • object with required properties and additionalProperties: false
  • array with a single items schema
  • enum with string values
  • anyOf for optional fields (e.g., anyOf: [{type: "string"}, {type: "null"}])
  • Nested objects and arrays up to 5 levels deep
Optional fields must be expressed using anyOf with a null type, not by omitting the field from required. All fields must be listed in required when strict: true is enabled.

Common use cases

1

Data extraction

Extract structured records from unstructured text — receipts, contracts, support tickets — and write them directly to a database without a parsing layer.
class Receipt(BaseModel):
    vendor: str
    total: float
    currency: str
    line_items: list[str]

completion = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract the receipt details."},
        {"role": "user", "content": receipt_text},
    ],
    response_format=Receipt,
)
receipt = completion.choices[0].message.parsed
2

UI rendering

Generate structured content that maps directly to UI components — steps, cards, tables — without post-processing.
3

Multi-agent handoffs

Use Structured Outputs to define strict contracts between agents in a pipeline, ensuring each agent passes exactly the data the next one expects.

Next steps

Responses API intro

Learn about the stateful Responses API with built-in tools for web search, file search, and code interpreter.

File search

Upload documents to a vector store and query them with the file search tool in the Responses API.

Build docs developers (and LLMs) love