Claude API JSON Structured Output

Get structured JSON from Claude without tool use. Use system prompts + temperature=0 to extract entities, classify text, and parse structured data.

💥 50p impulse-buy: Power Prompts PDF (first 10 buyers) 30 battle-tested Claude Code prompts · 8-page PDF · paste into CLAUDE.md and never re-type a prompt again · 50p impulse-buy, no commitment

Two patterns for getting structured JSON from Claude: prompt-based (simple) and tool-based (schema-validated).

Pattern 1: Prompt-based JSON extraction

import anthropic
import json

client = anthropic.Anthropic()

def extract_json(text: str, schema_desc: str) -> dict:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        temperature=0,
        system=f"""Extract structured data from text and return ONLY valid JSON.
Schema: {schema_desc}
Rules:
- Return only the JSON object, no markdown, no explanation
- Use null for missing fields
- Follow the exact field names in the schema""",
        messages=[{"role": "user", "content": text}]
    )
    return json.loads(response.content[0].text)

# Example: extract contact info
result = extract_json(
    "Call Sarah Johnson at sarah.j@company.com or +1-555-0123 for the Q3 budget review.",
    '{"name": string, "email": string, "phone": string, "topic": string}'
)
print(result)
# {"name": "Sarah Johnson", "email": "sarah.j@company.com", "phone": "+1-555-0123", "topic": "Q3 budget review"}

Pattern 2: Tool-based schema enforcement

def extract_with_tool(text: str) -> dict:
    tool = {
        "name": "extract_contact",
        "description": "Extract contact information from text.",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "email": {"type": "string", "format": "email"},
                "phone": {"type": ["string", "null"]},
                "topic": {"type": "string"}
            },
            "required": ["name", "email", "topic"]
        }
    }

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=512,
        tools=[tool],
        tool_choice={"type": "tool", "name": "extract_contact"},
        messages=[{"role": "user", "content": text}]
    )

    for block in response.content:
        if block.type == "tool_use":
            return block.input
    return {}

result = extract_with_tool("Please contact John Smith (jsmith@corp.io) re: contract renewal.")
print(result)

Classify text into categories

def classify(text: str, categories: list[str]) -> dict:
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",  # fast and cheap for classification
        max_tokens=64,
        temperature=0,
        system=f'Classify the text. Return JSON: {{"category": one of {categories}, "confidence": 0.0-1.0, "reason": "brief"}}. Only JSON, no explanation.',
        messages=[{"role": "user", "content": text}]
    )
    return json.loads(response.content[0].text)

result = classify("My bill was charged twice!", ["billing", "technical", "shipping", "general"])
print(result)
# {"category": "billing", "confidence": 0.98, "reason": "Duplicate charge complaint"}

See the full tool use guide for complex multi-step agent loops. Haiku 4.5 is cheapest for high-volume classification — see Haiku pricing.

Frequently asked questions

Does Claude have a native JSON mode like GPT-4o?
Claude does not have a built-in 'JSON mode' toggle in the same way as some other APIs. However, instructing Claude to output JSON via the system prompt and setting temperature=0 is highly reliable. For guaranteed schema adherence, use tool_use with a tool whose input_schema matches your desired structure.
Should I use tool_use or prompt engineering for JSON output?
For simple extraction (named fields, no nested arrays), prompt engineering with temperature=0 is simpler. For complex schemas, enum fields, or required validation, define a tool with an input_schema — Claude will populate it with correct types.
How do I handle JSON parsing errors from Claude?
Wrap `json.loads()` in a try/except. If it fails, re-prompt with the error: 'Your previous response was not valid JSON. Error: {e}. Return only the JSON object.' One retry almost always resolves it.

Free tools

Cost Calculator → API Cookbook → Diff Summarizer → Skills Browser →

More examples

Claude API Python QuickstartClaude API Node.js / TypeScript QuickstartClaude API Streaming in PythonClaude API Streaming in Node.js / TypeScriptClaude API Tool Use in PythonClaude API Tool Use in Node.js / TypeScript