May 27, 2026

OpenAI structured outputs JSON schema: a practical guide

Reading time :  
7
 min
Rebecca Pearson
Rebecca Pearson

OpenAI structured outputs JSON schema: a practical guide

OpenAI structured outputs let you define a JSON schema that the model must follow when generating a response. Instead of hoping the LLM returns valid JSON and parsing it with crossed fingers, you get guaranteed schema compliance. The model's output conforms to your types, your field names, your constraints — every time.

This is not JSON mode (which merely guarantees valid JSON without schema enforcement). Structured outputs go further: you supply a JSON schema, and the model's token generation is constrained to produce output matching that schema exactly. OpenAI's documentation confirms that structured outputs achieve 100% schema compliance in their evaluations, compared to roughly 86% for function calling and lower for raw JSON mode.

According to a 2025 Stack Overflow developer survey, 78% of developers using LLM APIs reported parsing failures or unexpected output formats as a recurring pain point. Structured outputs eliminate that category of failure entirely.

Unlike generic AI automation posts, this guide shows real CodeWords workflows — not just theory.

TL;DR

  • OpenAI structured outputs enforce a JSON schema on the model's response, guaranteeing type-safe, parseable output with 100% schema compliance.
  • Define schemas using the response_format parameter with type: "json_schema" — supports objects, arrays, enums, optional fields, and nested structures.
  • CodeWords uses structured outputs in extraction, classification, and data transformation workflows where reliable parsing is non-negotiable.

How do structured outputs differ from JSON mode?

Three approaches exist for getting structured data from OpenAI models. Understanding the differences prevents wasted debugging time.

Raw text output (default): The model generates free-form text. You hope it includes JSON and try to parse it. Parsing fails ~15-25% of the time depending on prompt complexity. Not production-viable for data pipelines.

JSON mode (response_format: {"type": "json_object"}): The model guarantees valid JSON. Fields, types, and structure are not guaranteed. The model might return {"answer": "yes"} when you expected {"is_approved": true, "reason": "..."}. Better, still fragile.

Structured outputs (response_format: {"type": "json_schema", "json_schema": {...}}): The model guarantees output matching your exact schema. Field names, types, required properties, enum values — all enforced. This is the correct choice for any production data pipeline.

Think of it as the difference between telling someone "respond in English" (JSON mode) and handing them a form with labeled fields (structured outputs). The form guarantees you get exactly what you need.

How do you define a JSON schema for structured outputs?

The schema follows JSON Schema conventions with some OpenAI-specific constraints.

Basic example — extracting event data:

import openai

response = openai.chat.completions.create(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "Extract event details from the text."},
        {"role": "user", "content": "Let's meet at Blue Bottle Coffee on Thursday at 2pm to discuss the Q3 roadmap."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "event_extraction",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "location": {"type": "string"},
                    "date_reference": {"type": "string"},
                    "time": {"type": "string"},
                    "participants": {
                        "type": "array",
                        "items": {"type": "string"}
                    },
                    "category": {
                        "type": "string",
                        "enum": ["meeting", "social", "deadline", "reminder", "other"]
                    }
                },
                "required": ["title", "location", "date_reference", "time", "participants", "category"],
                "additionalProperties": False
            }
        }
    }
)

import json
event = json.loads(response.choices[0].message.content)

The response is guaranteed to contain all required fields with correct types. category will always be one of the enum values. participants will always be an array of strings.

What are the schema constraints and limitations?

OpenAI's structured output implementation has specific requirements:

Supported types: string, number, integer, boolean, array, object, null. Union types via anyOf are supported for nullable fields.

All fields must be required. When strict: true, every property in an object must be listed in required. Use anyOf with null type for optional fields:

"properties": {
    "notes": {
        "anyOf": [
            {"type": "string"},
            {"type": "null"}
        ]
    }
}

additionalProperties must be false for every object in strict mode. This prevents the model from adding unexpected fields.

Nesting depth limit: Schemas support up to 5 levels of nesting. Flatten deeply nested structures if needed.

No pattern or format validation: JSON Schema keywords like pattern, minLength, minimum, and format are not enforced by the model. The schema constrains structure, not content. Validate content in your application code.

For more on managing these constraints in production, see OpenAI API limits for throughput considerations and AI-powered code generation tools for code-specific schema patterns.

What are practical schema patterns for common workflows?

Pattern 1 — Classification with confidence:

classification_schema = {
    "type": "object",
    "properties": {
        "category": {
            "type": "string",
            "enum": ["bug_report", "feature_request", "question", "complaint", "praise"]
        },
        "confidence": {
            "type": "string",
            "enum": ["high", "medium", "low"]
        },
        "reasoning": {"type": "string"},
        "suggested_assignee": {"type": "string"}
    },
    "required": ["category", "confidence", "reasoning", "suggested_assignee"],
    "additionalProperties": False
}

This powers support ticket routing in CodeWords workflows. The enum constraint means downstream routing logic never encounters an unexpected category. See AI workflow automation for the classify-and-route pattern.

Pattern 2 — Multi-entity extraction:

extraction_schema = {
    "type": "object",
    "properties": {
        "entities": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "type": {"type": "string", "enum": ["person", "company", "product", "location"]},
                    "context": {"type": "string"}
                },
                "required": ["name", "type", "context"],
                "additionalProperties": False
            }
        },
        "summary": {"type": "string"}
    },
    "required": ["entities", "summary"],
    "additionalProperties": False
}

Pattern 3 — Decision with structured reasoning:

decision_schema = {
    "type": "object",
    "properties": {
        "decision": {"type": "string", "enum": ["approve", "reject", "escalate"]},
        "factors": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "factor": {"type": "string"},
                    "weight": {"type": "string", "enum": ["high", "medium", "low"]},
                    "assessment": {"type": "string"}
                },
                "required": ["factor", "weight", "assessment"],
                "additionalProperties": False
            }
        },
        "next_action": {"type": "string"}
    },
    "required": ["decision", "factors", "next_action"],
    "additionalProperties": False
}

This pattern is central to custom AI agent development — agents that make structured decisions with auditable reasoning chains.

How do you use structured outputs with the Python SDK?

The OpenAI Python SDK (v1.40+) supports structured outputs natively with Pydantic models, which is cleaner than raw dictionaries.

from pydantic import BaseModel
from openai import OpenAI

class CalendarEvent(BaseModel):
    title: str
    date: str
    time: str
    duration_minutes: int
    attendees: list[str]
    is_recurring: bool

client = OpenAI()

completion = client.beta.chat.completions.parse(
    model="gpt-4o-2024-08-06",
    messages=[
        {"role": "system", "content": "Extract calendar event details."},
        {"role": "user", "content": "Weekly team standup every Monday at 9am, 30 minutes, with Alice and Bob"}
    ],
    response_format=CalendarEvent
)

event = completion.choices[0].message.parsed
print(event.title)
print(event.attendees)

The .parse() method handles schema generation from the Pydantic model and response deserialization. The result is a typed Python object, not a raw string.

In CodeWords, Cody generates these Pydantic models as part of the workflow scaffold. You describe the data shape conversationally, Cody creates the model, and the extraction workflow runs as a serverless microservice. See CodeWords templates for pre-built extraction workflows.

How do structured outputs fit into larger workflows?

Structured outputs are most valuable when they feed downstream systems. The schema becomes a contract between the LLM step and everything after it.

Workflow: Invoice processing pipeline 1. Firecrawl or email integration extracts invoice PDFs 2. LLM with structured output extracts line items, totals, vendor info 3. Validation step checks totals match line items 4. Data pushes to Airtable or accounting software 5. Anomalies flag to Slack for review

Without structured outputs, step 2 produces unpredictable JSON that breaks step 3. With structured outputs, the schema guarantees that line_items is always an array of objects with description, quantity, and amount fields. The validation step never encounters a missing field.

For building these multi-step pipelines, see workflow automation tools and no-code workflow automation.

FAQ

Which OpenAI models support structured outputs?

Structured outputs are supported by GPT-4o (2024-08-06 and later), GPT-4o-mini, and newer models. Earlier GPT-4 versions do not support the json_schema response format. Check OpenAI's model documentation for the latest compatibility list.

Do structured outputs cost more than regular API calls?

No. Structured outputs use the same per-token pricing as regular chat completions. The schema constraint is applied during generation without additional cost. Schema processing adds a small latency overhead on the first request with a new schema (cached afterward).

Can I use structured outputs with streaming?

Yes. Structured outputs work with streaming responses. The streamed tokens are guaranteed to produce valid JSON matching your schema when assembled. The Python SDK's .stream() and .parse() methods handle this.

How do structured outputs compare to function calling?

Function calling was the original approach to getting structured data from OpenAI models, achieving ~86% schema compliance. Structured outputs achieve 100% compliance. Use structured outputs for data extraction and formatted responses. Use function calling when you want the model to decide which tool to invoke, not just format data.

Structured data as a design primitive

Structured outputs transform LLMs from text generators into typed data extractors. The implication is architectural: every workflow step that consumes LLM output can now rely on a schema contract. Error handling shifts from "did the LLM return valid data?" to "is the data semantically correct?" — a much more tractable problem.

Start building extraction and classification workflows with structured outputs on CodeWords — type-safe LLM responses, managed execution, and 500+ integrations for your data pipeline.

Contents
Ready to try CodeWords?
Get started free
Sign in
Sign in