Login
Back to Blog
Structured Output & JSON Mode: Get Reliable AI Responses Across Providers

Structured Output & JSON Mode: Get Reliable AI Responses Across Providers

C
Crazyrouter Team
March 2, 2026
513 viewsEnglishGuide
Share:

Structured Output & JSON Mode: Get Reliable AI Responses Across Providers#

One of the biggest challenges in building AI-powered applications is getting models to return data in a consistent, parseable format. Structured output and JSON mode solve this by constraining model responses to valid JSON that matches your schema.

This guide compares how different AI providers implement structured output and shows you how to use each one effectively.

What is Structured Output?#

Structured output forces an AI model to return responses in a specific format — typically JSON conforming to a JSON Schema. Instead of hoping the model returns valid JSON, the API guarantees it.

Why It Matters#

code
Without structured output:
  User: "Extract the name and email from this text"
  AI: "The name is John Smith and the email is john@example.com"
  → You need regex/parsing to extract data 😩

With structured output:
  AI: {"name": "John Smith", "email": "john@example.com"}
  → Direct JSON.parse(), ready to use ✅

Key Benefits#

  • 100% valid JSON — no parsing failures in production
  • Schema enforcement — responses match your exact data model
  • Type safety — numbers are numbers, arrays are arrays
  • Reduced hallucination — constrained output space limits fabrication

Provider Comparison#

FeatureOpenAIAnthropic (Claude)Google (Gemini)DeepSeek
JSON Moderesponse_format: {type: "json_object"}✅ Via system promptresponse_mime_typeresponse_format
Schema Enforcementjson_schema type⚠️ Prompt-basedresponse_schema⚠️ Prompt-based
Strict Modestrict: true
Tool-based✅ Function calling✅ Tool use✅ Function calling✅ Function calling
Streaming✅ Partial JSON
Nested Objects
Arrays
Enums⚠️ Limited

Implementation Guide#

python
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
    api_key="YOUR_CRAZYROUTER_KEY",
    base_url="https://crazyrouter.com/v1"
)

# Define your schema with Pydantic
class ProductReview(BaseModel):
    product_name: str
    rating: float
    pros: list[str]
    cons: list[str]
    summary: str
    recommended: bool

# Use structured output - guaranteed to match schema
response = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract a structured product review from the text."},
        {"role": "user", "content": "I bought the Sony WH-1000XM6 headphones last month. Amazing noise cancellation, super comfortable for long flights. Battery lasts 40 hours which is insane. The app is a bit clunky though, and they're pricey at $399. Sound quality is phenomenal, especially for classical music. Highly recommend if you can afford them. 9/10."}
    ],
    response_format=ProductReview
)

review = response.choices[0].message.parsed
print(f"Product: {review.product_name}")
print(f"Rating: {review.rating}/10")
print(f"Recommended: {review.recommended}")
print(f"Pros: {', '.join(review.pros)}")

OpenAI — JSON Schema (Raw)#

python
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Extract events from the text as JSON."},
        {"role": "user", "content": "The conference is March 15-17 in San Francisco. Keynote by Sam Altman on March 15 at 9 AM. Workshop on fine-tuning March 16 at 2 PM."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "events",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "events": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "title": {"type": "string"},
                                "date": {"type": "string"},
                                "time": {"type": "string"},
                                "location": {"type": "string"}
                            },
                            "required": ["title", "date"],
                            "additionalProperties": False
                        }
                    }
                },
                "required": ["events"],
                "additionalProperties": False
            }
        }
    }
)

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

Claude — Tool-Based Structured Output#

python
import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_CRAZYROUTER_KEY",
    base_url="https://crazyrouter.com/v1"
)

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[{
        "name": "extract_review",
        "description": "Extract a structured product review",
        "input_schema": {
            "type": "object",
            "properties": {
                "product_name": {"type": "string"},
                "rating": {"type": "number", "minimum": 0, "maximum": 10},
                "pros": {"type": "array", "items": {"type": "string"}},
                "cons": {"type": "array", "items": {"type": "string"}},
                "summary": {"type": "string"},
                "recommended": {"type": "boolean"}
            },
            "required": ["product_name", "rating", "pros", "cons", "summary", "recommended"]
        }
    }],
    tool_choice={"type": "tool", "name": "extract_review"},
    messages=[{
        "role": "user",
        "content": "Extract a review from: Great laptop, the M4 chip is blazing fast. Battery life is incredible at 18 hours. A bit heavy at 4.7 lbs. The keyboard could be better. 8.5/10, recommended for developers."
    }]
)

# The response is guaranteed valid JSON matching the schema
review_data = response.content[0].input
print(review_data)

Gemini — Response Schema#

python
import google.generativeai as genai

genai.configure(api_key="YOUR_API_KEY")

model = genai.GenerativeModel("gemini-2.5-flash")

response = model.generate_content(
    "Extract the recipe from: Mix 2 cups flour, 1 cup sugar, 3 eggs. Bake at 350°F for 30 minutes.",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema={
            "type": "object",
            "properties": {
                "recipe_name": {"type": "string"},
                "ingredients": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "item": {"type": "string"},
                            "amount": {"type": "string"}
                        }
                    }
                },
                "instructions": {"type": "array", "items": {"type": "string"}},
                "cook_time_minutes": {"type": "integer"},
                "temperature_f": {"type": "integer"}
            }
        }
    )
)

import json
recipe = json.loads(response.text)

Universal Approach with Crazyrouter#

python
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_CRAZYROUTER_KEY",
    base_url="https://crazyrouter.com/v1"
)

# Works with ANY model through Crazyrouter
models = ["gpt-4o", "claude-sonnet-4-20250514", "gemini-2.5-flash"]

for model in models:
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": "Always respond in JSON format with keys: sentiment, confidence, keywords"},
            {"role": "user", "content": "Analyze: The new AI features are amazing but the pricing is too high"}
        ],
        response_format={"type": "json_object"}
    )
    print(f"{model}: {response.choices[0].message.content}")

Best Practices#

1. Always Validate Responses#

python
import json
from jsonschema import validate

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "score": {"type": "number", "minimum": 0, "maximum": 100}
    },
    "required": ["name", "score"]
}

data = json.loads(response.choices[0].message.content)
validate(instance=data, schema=schema)  # Raises if invalid

2. Use Enums for Categorical Data#

python
response_format={
    "type": "json_schema",
    "json_schema": {
        "name": "classification",
        "schema": {
            "type": "object",
            "properties": {
                "category": {
                    "type": "string",
                    "enum": ["bug", "feature", "question", "documentation"]
                },
                "priority": {
                    "type": "string",
                    "enum": ["low", "medium", "high", "critical"]
                }
            }
        }
    }
}

3. Handle Streaming JSON#

python
# For streaming, collect chunks and parse at the end
full_content = ""
for chunk in client.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    response_format={"type": "json_object"},
    stream=True
):
    if chunk.choices[0].delta.content:
        full_content += chunk.choices[0].delta.content

result = json.loads(full_content)

FAQ#

What's the difference between JSON mode and structured output?#

JSON mode guarantees the output is valid JSON but doesn't enforce a specific schema. Structured output (available in OpenAI and Gemini) additionally guarantees the JSON matches your exact schema with correct types, required fields, and constraints.

Which AI provider has the best structured output support?#

OpenAI currently leads with the most complete implementation including strict schema enforcement and Pydantic integration. Claude's tool-based approach is also reliable. Gemini's response_schema is effective but slightly less flexible. All are accessible through Crazyrouter with a unified API.

Does structured output increase latency?#

Slightly. Schema enforcement adds a small overhead (typically 50-200ms) due to constrained decoding. For most applications, this is negligible compared to the reliability gains.

Can I use structured output with streaming?#

Yes, all major providers support streaming with JSON mode. The model streams partial JSON tokens, and you concatenate them into a complete JSON object once the stream ends. OpenAI also supports partial JSON parsing during streaming.

How does structured output handle optional fields?#

In OpenAI's strict mode, all properties must be listed in required, and optional values should use "type": ["string", "null"] (nullable types). Claude's tool schema supports standard JSON Schema optional fields normally.

Summary#

Structured output is essential for building reliable AI-powered data pipelines. Whether you're extracting information, classifying content, or generating structured data, JSON mode and schema enforcement eliminate the fragility of free-text parsing.

With Crazyrouter, you can access structured output across all major providers — OpenAI, Claude, Gemini, DeepSeek, and more — through one unified API, with 25-30% cost savings.

Build reliable AI data pipelinesGet your Crazyrouter API key

Implementation Guides

Related Posts

AI API Token Cost Calculator: How to Estimate and Optimize Your AI SpendingGuide

AI API Token Cost Calculator: How to Estimate and Optimize Your AI Spending

"Learn how to calculate AI API costs, estimate token usage, and optimize spending across GPT-5, Claude, Gemini, and other models. Includes a practical cost calculator approach."

Feb 26
AI Guardrails: How to Build Safe AI Applications with API Safety Layers in 2026Guide

AI Guardrails: How to Build Safe AI Applications with API Safety Layers in 2026

"Complete guide to implementing AI guardrails for production applications. Learn content filtering, prompt injection defense, output validation, and safety layers across GPT-5, Claude, and Gemini APIs."

Mar 13
Google Veo3 API Guide 2026: Production Video Generation with Safer FallbacksGuide

Google Veo3 API Guide 2026: Production Video Generation with Safer Fallbacks

A production-oriented Google Veo3 API guide with code examples, prompt structure, quality control, and fallback patterns for real video generation systems.

Mar 19
Claude Code Pricing Guide 2026: CI Agents, Team Seats, and API Budget PlanningGuide

Claude Code Pricing Guide 2026: CI Agents, Team Seats, and API Budget Planning

A developer-focused Claude Code pricing guide for teams running local coding agents, CI automation, and production API fallbacks in 2026.

May 23
Qwen2.5-Omni Guide 2026: Real-Time Voice, Vision, and Agent WorkflowsGuide

Qwen2.5-Omni Guide 2026: Real-Time Voice, Vision, and Agent Workflows

A developer-focused qwen2.5-omni guide article covering what it is, alternatives, API examples, pricing, FAQs, and when to use Crazyrouter for unified routing.

Jun 6
Seedance 2.0 API Pricing: ByteDance Video AI Costs, Limits & Budget Guide 2026Guide

Seedance 2.0 API Pricing: ByteDance Video AI Costs, Limits & Budget Guide 2026

"Complete Seedance 2.0 pricing breakdown — per-video costs, API rate limits, resolution tiers, and how to optimize spend on ByteDance's video generation model with routing through Crazyrouter."

Apr 13