Artificial IntelligenceAnthropicClaude API

Structured Outputs with Claude: Getting JSON Every Time

TT
TopicTrick
Structured Outputs with Claude: Getting JSON Every Time

Every real production application that uses an AI model faces the same fundamental challenge: the AI speaks in natural language, but your code needs structured data. You need a list, not a paragraph. You need a JSON object with specific fields, not a conversational reply. You need machine-readable output that you can parse, validate, and store reliably.

Claude is highly capable of producing structured output, but it requires intentional design. Left to its defaults, Claude will produce well-written prose. This guide covers every technique available for extracting structured, schema-compliant JSON from Claude — from simple prompt engineering to using the tool-use API as a structured output mechanism.


Why Structured Output Matters in Production

Consider a typical AI-powered feature: a resume parser that extracts candidate data from uploaded CVs. You might want Claude to extract the candidate's name, email, work history, and skills. If Claude returns a friendly paragraph summarising the candidate, your application cannot process it. You need a JSON object.

The same challenge applies to:

  • Classification systems that must return one of a fixed set of labels
  • Sentiment analysis pipelines that feed scores to analytics databases
  • Data extraction workflows that populate CRM fields from unstructured text
  • API orchestration layers that route based on Claude's decision output

Getting structured output right is not optional — it is the difference between a demo and a production system.


Technique 1 — Prompt Constraints (Simplest Approach)

The simplest approach is to instruct Claude explicitly in your system prompt to return only JSON and nothing else.

python
1import anthropic 2import json 3 4client = anthropic.Anthropic() 5 6system_prompt = """You are a data extraction assistant. 7You MUST respond ONLY with valid JSON. Do not include any explanation, 8preamble, or markdown code fences. Return raw JSON only. 9The JSON must match this schema exactly: 10\{ 11 "sentiment": "positive" | "negative" | "neutral", 12 "confidence": 0.0 to 1.0, 13 "key_topics": ["string", "string"] 14}""" 15 16response = client.messages.create( 17 model="claude-sonnet-4-6", 18 max_tokens=1024, 19 system=system_prompt, 20 messages=[{ 21 "role": "user", 22 "content": "Analyse this review: 'The product arrived quickly but the quality was disappointing for the price.'" 23 }] 24) 25 26output = json.loads(response.content[0].text) 27print(output) 28# {"sentiment": "negative", "confidence": 0.82, "key_topics": ["delivery", "quality", "price"]}

This works well for simple schemas and in development. However, for complex schemas or in high-volume production, you should use the tool-use approach below for stronger reliability guarantees.

Never Blindly Parse AI Output

Always wrap json.loads() in a try/except block. Even with strict instructions, edge cases can produce invalid JSON — particularly for complex documents or unusual inputs. Implement a validation step after parsing to confirm the data matches your expected schema before using it in downstream systems.


    Technique 2 — Assistant Prefill

    Assistant prefill is a powerful technique where you begin Claude's response for it. By starting the response with an opening brace, you force Claude to complete a JSON object.

    python
    1response = client.messages.create( 2 model="claude-sonnet-4-6", 3 max_tokens=1024, 4 system="Extract the requested data as JSON only.", 5 messages=[ 6 { 7 "role": "user", 8 "content": "Extract name, role, and company from: 'Hi, I'm Sarah Chen, a Senior DevOps Engineer at Meridian Systems.'" 9 }, 10 { 11 "role": "assistant", 12 "content": "\{" # Claude must continue from here 13 } 14 ] 15) 16 17# Claude's response continues from the \{ we provided 18raw_json = "\{" + response.content[0].text 19output = json.loads(raw_json) 20# {"name": "Sarah Chen", "role": "Senior DevOps Engineer", "company": "Meridian Systems"}

    This technique works because Claude cannot insert explanatory text before the JSON — the opening brace has already been placed. It is reliable for straightforward extraction tasks.


    Technique 3 — Tool Use for Structured Output (Production-Grade)

    The most reliable technique for getting structured output from Claude is using the tool-use API in an unconventional but highly effective way: you define a tool with your desired output schema, and Claude is forced to call that tool with valid arguments matching your schema.

    This works because Claude's tool-calling mechanism inherently produces structured, schema-validated output. Claude cannot return free-form text when it is in the process of calling a tool.

    python
    1import anthropic 2import json 3 4client = anthropic.Anthropic() 5 6# Define your desired output shape as a tool 7extract_candidate_tool = { 8 "name": "extract_candidate_data", 9 "description": "Extract structured candidate information from a CV or resume text", 10 "input_schema": { 11 "type": "object", 12 "properties": { 13 "full_name": { 14 "type": "string", 15 "description": "The candidate's full name" 16 }, 17 "email": { 18 "type": "string", 19 "description": "Email address" 20 }, 21 "years_experience": { 22 "type": "number", 23 "description": "Total years of professional experience" 24 }, 25 "skills": { 26 "type": "array", 27 "items": {"type": "string"}, 28 "description": "List of technical skills mentioned" 29 }, 30 "current_role": { 31 "type": "string", 32 "description": "Most recent job title" 33 } 34 }, 35 "required": ["full_name", "email", "skills"] 36 } 37} 38 39response = client.messages.create( 40 model="claude-sonnet-4-6", 41 max_tokens=1024, 42 tools=[extract_candidate_tool], 43 tool_choice={"type": "any"}, # Force Claude to use a tool 44 messages=[{ 45 "role": "user", 46 "content": cv_text # Your resume text here 47 }] 48) 49 50# Extract the structured data from the tool call 51for block in response.content: 52 if block.type == "tool_use": 53 candidate_data = block.input 54 print(candidate_data)

    The tool_choice: {"type": "any"} parameter is critical — it forces Claude to call one of the provided tools rather than responding in plain text.

    Tool Use for Output, Not Just Actions

    Most developers think of tool use as a mechanism for giving Claude the ability to call external APIs — web search, calculators, databases. But tool use is equally valuable as a structured output mechanism. The input_schema in your tool definition acts as a JSON Schema validator, and Claude is constrained to produce valid arguments matching that schema. This is the most reliable path to structured output in production.


      Technique 4 — Chain of Thought Before JSON

      For complex extraction tasks where raw JSON instructions cause Claude to miss nuance, you can use a two-step approach: first ask Claude to think through the problem in prose, then extract the final answer as JSON.

      python
      1# Step 1: Analysis pass 2analysis_response = client.messages.create( 3 model="claude-sonnet-4-6", 4 max_tokens=2048, 5 messages=[{ 6 "role": "user", 7 "content": f"Analyse this contract for key obligations, parties, and risk factors:\n\n{contract_text}" 8 }] 9) 10 11analysis = analysis_response.content[0].text 12 13# Step 2: Structured extraction 14extraction_response = client.messages.create( 15 model="claude-sonnet-4-6", 16 max_tokens=1024, 17 tools=[contract_extraction_tool], 18 tool_choice={"type": "any"}, 19 messages=[ 20 {"role": "user", "content": f"Contract: {contract_text}"}, 21 {"role": "assistant", "content": analysis}, 22 {"role": "user", "content": "Now extract the key data into the structured format."} 23 ] 24)

      This approach gives Claude the reasoning space to understand the content deeply before committing it to a rigid schema.


      Defining Robust JSON Schemas

      The quality of your output depends heavily on your schema definition. A vague schema produces inconsistent output.

      Good Schema Practices

      • Be specific about types: Use "type": "string", "type": "number", "type": "boolean", "type": "array" explicitly
      • Add clear descriptions: Each field's "description" property should explain exactly what data belongs there — Claude reads these when filling values
      • Use enums for fixed values: "enum": ["low", "medium", "high"] constrains the output to valid options
      • Mark required fields: The "required" array ensures Claude always populates your critical fields even when the data is not present in the source
      • Handle missing data: Define a null-able or optional field for data that may not be in the source rather than allowing Claude to invent values

      Validation After Extraction

      Never trust raw output — validate it against your schema before use.

      python
      1from jsonschema import validate, ValidationError 2 3schema = { 4 "type": "object", 5 "properties": { 6 "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]}, 7 "confidence": {"type": "number", "minimum": 0.0, "maximum": 1.0} 8 }, 9 "required": ["sentiment", "confidence"] 10} 11 12try: 13 validate(instance=output, schema=schema) 14 # Output is valid — proceed 15except ValidationError as e: 16 # Log the error and handle gracefully 17 print(f"Validation failed: {e.message}")

      Log Invalid Outputs for Prompt Refinement

      When validation fails in production, log the raw output alongside the input that triggered it. These failure cases are gold for improving your schema definitions and system prompt. A pattern of failures on a particular field type or input category tells you exactly where your prompt or schema needs strengthening.


        Summary

        Getting reliable structured output from Claude is one of the most critical skills in building production AI applications. The four techniques in this guide — from simple to most reliable:

        1. Prompt constraints: Simplest, fine for development and low-volume tasks
        2. Assistant prefill: Reliable for straightforward extraction with known structure
        3. Tool use as output mechanism: Most reliable, production-grade, schema-enforced
        4. Chain of thought then extract: Best for complex documents requiring understanding before extraction

        In the next post, we pause before diving into advanced features to consolidate everything covered so far on prompt engineering. It is a rapid-fire refresher of the ten techniques every Claude developer should have at their fingertips: Prompt Engineering Refresher: 10 Techniques Every Developer Should Know.


        This post is part of the Anthropic AI Tutorial Series. Previous post: Claude Extended Thinking: How to Unlock Deep Reasoning.