Introduction to Structured Outputs
When building applications powered by Large Language Models (LLMs), developers often need the model's output to be machine-readable. Traditionally, developers prompted the model to output JSON and then used regular expressions or JSON parsers to extract the data. However, models would occasionally output malformed JSON, include conversational filler, or omit required fields. OpenAI's Structured Outputs feature solves this problem by guaranteeing that the model's response will conform to a provided JSON Schema.
What are Structured Outputs?
Structured Outputs is a feature in the OpenAI API that allows developers to pass a JSON Schema as part of the request. The model is then constrained to generate a response that strictly adheres to that schema. Instead of hoping the model outputs valid JSON, the API enforces it at the decoding level, ensuring the output is syntactically correct and semantically aligned with your defined types, properties, and required fields.
Why Structured Outputs Matter
Enforcing a JSON Schema provides several critical benefits for production applications:
- Reliability: Eliminates parsing errors caused by missing brackets, trailing commas, or unexpected text wrapping the JSON payload.
- Reduced Hallucinations: By explicitly defining required fields and their types, the model is less likely to invent irrelevant properties.
- Simplified Integration: Downstream systems can confidently map the API response directly to strongly typed objects in your programming language (like Python dataclasses or TypeScript interfaces) without writing complex fallback logic.
- Constrained Choices: You can use enums to force the model to choose from a specific list of values, preventing unexpected categorical outputs.
Getting Started with JSON Schema Enforcement
To use Structured Outputs, you must use a compatible model (such as gpt-4o-2024-08-06 or later) and define your schema within the response_format parameter of your API call. You will set the type to json_schema and provide the schema definition.
Basic Example: Defining a Schema
Let's imagine we are building an application that extracts user information from unstructured text. We want the model to return a JSON object containing the user's name, age, and email address.
from openai import OpenAI
import json
client = OpenAI(api_key="your-api-key")
# Define the JSON Schema
user_schema = {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The full name of the user."
},
"age": {
"type": "integer",
"description": "The age of the user in years."
},
"email": {
"type": "string",
"description": "The user's email address."
}
},
"required": ["name", "age", "email"],
"additionalProperties": False
}
response = client.chat.completions.create(
model="gpt-4o-2024-08-06",
messages=[
{
"role": "system",
"content": "Extract the user's information from the provided text."
},
{
"role": "user",
"content": "John Doe is 32 years old. You can reach him at john.doe@example.com."
}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "user_extraction",
"schema": user_schema,
"strict": True
}
}
)
# Parse the guaranteed JSON response
extracted_data = json.loads(response.choices[0].message.content)
print(extracted_data)
In this example, the strict: True flag is crucial. It tells the OpenAI API to strictly enforce the schema, guaranteeing that the output will not contain any properties not defined in the schema and that all required fields will be present.
Advanced Schema Techniques
JSON Schema is highly expressive. You can enforce complex data structures, including nested objects, arrays, and constrained string values using enums.
Nested Objects and Arrays
If you need to extract a list of items or group related properties together, you can nest schemas. For example, if we want to extract a user's profile along with a list of their previous addresses, we can define an array of objects.
advanced_schema = {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"is_active": {
"type": "boolean"
},
"addresses": {
"type": "array",
"items": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
"zip_code": {"type": "string"}
},
"required": ["street", "city", "zip_code"],
"additionalProperties": False
}
}
},
"required": ["username", "is_active", "addresses"],
"additionalProperties": False
}
Using Enums for Constrained Choices
Sometimes you want the model to categorize data into predefined buckets. Using the enum keyword restricts the model's output to only the values you specify.
ticket_schema = {
"type": "object",
"properties": {
"ticket_id": {"type": "string"},
"category": {
"type": "string",
"enum": ["billing", "technical", "general", "feedback"]
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "urgent"]
}
},
"required": ["ticket_id", "category", "priority"],
"additionalProperties": False
}
Best Practices for Structured Outputs
To get the most out of Structured Outputs, consider the following best practices:
- Always use strict mode: Set
"strict": Truein yourresponse_format. Without it, the model may attempt to include additional properties or skip required ones, breaking your application's parsing logic. - Write clear descriptions: Even though the schema enforces the structure, the model still relies on natural language to understand what should go in each field. Use the
"description"property to guide the model accurately. - Keep schemas as simple as possible: While you can create deeply nested schemas, overly complex schemas can confuse the model and increase latency. Flatten structures where it makes logical sense.
- Handle optional fields correctly: If a field is not always present in the source text, omit it from the
"required"array. The model will either omit the key or returnnulldepending on your schema configuration. - Use Pydantic for Python development: If you are using the OpenAI Python SDK, consider defining your schemas using Pydantic models. The SDK provides a
client.beta.chat.completions.parsemethod that accepts a Pydantic model directly, automatically handling the JSON Schema generation and response parsing.
Conclusion
Structured Outputs represent a significant leap forward in building robust LLM applications. By enforcing a JSON Schema, developers can eliminate the fragility of prompt-based JSON extraction and build reliable pipelines that seamlessly integrate AI outputs into existing software systems. By understanding how to define clear schemas, leverage advanced features like enums and nested objects, and adhere to best practices, you can unlock highly predictable and production-ready behavior from OpenAI models.