Skip to main content
Structured Outputs let you enforce that a model’s response conforms to a JSON schema you define. Instead of asking the model to try to return JSON, the API guarantees the output matches your schema — no extra parsing, no malformed responses.
Structured Outputs are available on models that support response_format with json_schema (e.g. gpt-4o, gpt-4o-mini). For other models, use json_object mode with a system prompt.

JSON Mode vs. Structured Outputs

Featurejson_object modejson_schema mode
Guarantees valid JSON
Enforces specific keys
Supports required fields
Validates enums and types
Best forSimple JSON responsesProduction pipelines
Use json_schema when downstream code depends on specific fields always being present.

Basic Example

import os
import json
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
    base_url="https://api.routeway.ai/v1",
    api_key=os.getenv("ROUTEWAY_API_KEY")
)

# Define your schema with Pydantic
class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]
    location: str | None = None

response = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": "Alice and Bob are attending a machine learning workshop in Berlin on July 15th."
        }
    ],
    response_format=CalendarEvent,
)

event = response.choices[0].message.parsed
print(event.name)          # "Machine Learning Workshop"
print(event.participants)  # ["Alice", "Bob"]
print(event.date)          # "July 15th"
print(event.location)      # "Berlin"

Common Schema Patterns

Extraction

Pull structured records out of unstructured text.
class ProductReview(BaseModel):
    sentiment: Literal["positive", "neutral", "negative"]
    score: int          # 1-5
    key_issues: list[str]
    recommended: bool

response = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract a structured review from the customer feedback."},
        {"role": "user", "content": "I love the design but the battery only lasts 4 hours. Not worth the price."}
    ],
    response_format=ProductReview,
)

Classification

Route or label inputs reliably.
class SupportTicket(BaseModel):
    category: Literal["billing", "technical", "account", "general"]
    priority: Literal["low", "medium", "high", "urgent"]
    summary: str

response = client.beta.chat.completions.parse(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "Classify the support ticket."},
        {"role": "user", "content": "My card was charged twice for the same subscription last week."}
    ],
    response_format=SupportTicket,
)

Nested Objects

Schemas can be deeply nested.
class Address(BaseModel):
    street: str
    city: str
    country: str

class Contact(BaseModel):
    name: str
    email: str
    address: Address
    tags: list[str]

JSON Object Mode (Fallback)

For models that don’t support json_schema, use json_object mode with a system prompt instructing the model to return JSON.
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {
            "role": "system",
            "content": 'Return a JSON object with keys: "name" (string), "score" (number 1-10), "tags" (array of strings).'
        },
        {"role": "user", "content": "Analyze this product description: ..."}
    ],
    response_format={"type": "json_object"},
)

data = json.loads(response.choices[0].message.content)
json_object mode guarantees valid JSON but not schema conformance. Always validate the output against your expected shape before using it in production.

Schema Design Tips

Prevents the model from including unexpected fields that could break downstream parsers.
{
  "type": "object",
  "properties": {...},
  "required": ["name", "value"],
  "additionalProperties": false
}
With strict: true, every property must appear in required. Optional fields should use a union type like ["string", "null"].
"nickname": {"type": ["string", "null"]}
Enums are strictly enforced and reduce hallucination on fields with a known set of values.
"status": {"type": "string", "enum": ["active", "inactive", "pending"]}
Deeply nested schemas increase the chance of refusal on complex inputs. Flatten where the structure allows.