DEV Community

Sangmin Lee
Sangmin Lee

Posted on • Originally published at claudeguide.io

Claude API JSON Mode: Force JSON Output Every Time (2026)

Originally published at claudeguide.io/claude-api-json-mode

Claude doesn't have a dedicated "JSON mode" endpoint like OpenAI, but you can achieve near-100% reliable JSON output using 3 techniques: system prompt instruction (97% reliability), assistant prefilling (99.5%), and tool_choice force (100%). For claude-sonnet-4-5, the system prompt approach alone achieves 97%+ JSON compliance — add prefilling and you're at 99.5%.

Technique 1: System Prompt Instruction (Simplest)

The fastest path to JSON output:

import anthropic
import json

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system="""You are a data extraction API. Always respond with valid JSON only.
No explanatory text, no markdown code fences, no prefixes.
Your entire response must be parseable by json.loads().""",
    messages=[{
        "role": "user",
        "content": "Extract: name, email, company from: 'Hi, I'm Sarah Chen, sarah@acme.io, Acme Corp'"
    }]
)

data = json.loads(response.content[0].text)
# {"name": "Sarah Chen", "email": "sarah@acme.io", "company": "Acme Corp"}
Enter fullscreen mode Exit fullscreen mode

Works 97%+ of the time with claude-sonnet-4-5. The remaining 3% happens when Claude adds a brief explanation before the JSON. Fix that with prefilling.

Technique 2: Response Prefilling (Most Reliable)

Force JSON by starting the assistant's response with {:

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system="Extract structured data as JSON. Include all fields you find.",
    messages=[
        {"role": "user", "content": "Parse: Alice Johnson, Head of Engineering, alice@startup.io"},
        {"role": "assistant", "content": "{"}  # Prefill — Claude continues from here
    ]
)

# Response will be the JSON body (without the opening {)
raw = "{" + response.content[0].text
data = json.loads(raw)
Enter fullscreen mode Exit fullscreen mode

When you prefill with {, Claude must continue with valid JSON. It cannot prepend explanation text because the response already started. This brings compliance to 99.5%+.

Technique 3: Tool Use Enforcement (Best for Schemas)

Use tool use to enforce a specific JSON schema. Claude cannot return non-JSON when a tool call is required:

tools = [{
    "name": "extract_contact",
    "description": "Extract contact information from text",
    "input_schema": {
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "email": {"type": "string", "format": "email"},
            "company": {"type": "string"},
            "role": {"type": "string"}
        },
        "required": ["name"]
    }
}]

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "extract_contact"},  # Force this tool
    messages=[{"role": "user", "content": "Alice Johnson, Head of Engineering at Startup Inc"}]
)

# Get structured data from tool call
tool_block = next(b for b in response.content if b.type == "tool_use")
data = tool_block.input  # Already a dict, no json.loads() needed
# {"name": "Alice Johnson", "role": "Head of Engineering", "company": "Startup Inc"}
Enter fullscreen mode Exit fullscreen mode

Tool use with tool_choice: {type: "tool", name: "..."} achieves 100% schema compliance — Claude literally cannot return anything except valid tool arguments.

Choosing the Right Technique

Technique Compliance Schema Control Complexity Cost
System prompt 97% Loose Low Base
+ Prefilling 99.5% Loose Low Base
Tool use 100% Strict Medium +~15% tokens

Use system prompt + prefill when you need quick JSON without caring about exact schema enforcement.

Use tool use when you need strict schema validation, required fields, and type checking.

Production-Grade JSON Extraction

Combining all techniques with error handling:


python
import anthropic
import json
import re
from typing import TypeVar, Type
from pydantic import BaseModel

client = anthropic.Anthropic()
T = TypeVar('T', bound=BaseModel)

def extract_json(
    prompt: str,
    schema: Type[T],
    *,
    model: str = "claude-sonnet-4-5"
) -
Enter fullscreen mode Exit fullscreen mode

Top comments (0)