Structured Output

Tool use with a JSON schema is the reliable way to guarantee syntactically valid, schema-shaped output from Claude — but it prevents syntax errors, not semantic ones.

Lesson 21 of 3070% of the guide
Prefer to learn by doing?

When a downstream system needs machine-readable output, prompting Claude to *return JSON* is fragile — the model can still emit malformed or drifting structures. The reliable approach is tool use: you define a tool whose input_schema is the shape you want, and Claude returns a tool_use block whose input conforms to that schema. This eliminates JSON syntax errors entirely.

Reliability hierarchy

tool_use with a JSON schema > prompt-based 'please return JSON'. The first is guaranteed schema-shaped; the second can produce malformed JSON.

Parsing prose for JSON vs forced tool use
Don't

Asking for JSON in the prompt and parsing the reply is fragile — Claude can wrap it in prose, drift the shape, or emit malformed JSON.

messages=[{"role": "user",
  "content": "Extract the invoice as JSON."}]
# reply may include prose -> json.loads() throws
Do

Force a named tool whose input_schema is your target shape; Claude returns a tool_use block that is always schema-valid.

tools=[invoice_tool],
tool_choice={"type": "tool",
             "name": "extract_invoice"}
# msg.content -> tool_use.input conforms to schema

The three tool_choice modes

ModeBehaviourWhen to use
"auto"Model decides whether to call a tool or reply with textConversational replies are acceptable
"any"Model must call a tool but picks which oneSeveral schemas, unknown document type
{"type":"tool","name":"..."}Model must call one specific named toolMandatory step needing maximum control

For guaranteed extraction into a single known schema, force the specific tool. Use "any" when the input could match one of several schemas and you want the model to select. Reserve "auto" for cases where a plain text answer is a valid outcome.

import anthropic

client = anthropic.Anthropic()

invoice_tool = {
    "name": "extract_invoice",
    "description": "Extract structured fields from an invoice.",
    "input_schema": {
        "type": "object",
        "properties": {
            "invoice_number": {"type": "string"},
            "issue_date": {"type": "string", "description": "ISO 8601 (YYYY-MM-DD)"},
            "total": {"type": "number", "description": "Decimal amount, no currency symbol"},
            "currency": {"type": "string", "enum": ["USD", "EUR", "GBP", "unclear"]},
            "department": {"type": ["string", "null"], "description": "null if absent"}
        },
        "required": ["invoice_number", "total"]
    }
}

msg = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=[invoice_tool],
    tool_choice={"type": "tool", "name": "extract_invoice"},
    messages=[{"role": "user", "content": document_text}],
)
data = next(b.input for b in msg.content if b.type == "tool_use")

Syntax versus semantic errors

A schema constrains structure — field names, types, required keys — so the output is always parseable. It does not guarantee the values are correct. Semantic errors slip through: line items that do not sum to the stated total, a value placed in the wrong field, or a fabricated number for information that was never in the source. Catching those requires external validation logic, covered in the next lesson.

Exam trap

Do not assume tool_use prevents all extraction errors — it stops malformed JSON, not wrong values. And do not confuse tool_choice 'auto' (may reply in text) with 'any' (must call some tool).

Schema design that prevents fabrication

  • Optional/nullable fields — make a field nullable when the source may omit it, so the model reports absence instead of inventing a plausible value.
  • An 'unclear' enum value — give genuinely ambiguous categoricals an explicit escape hatch instead of forcing a guess.
  • 'other' plus a detail string — pair an 'other' enum with a freeform field to capture edge cases without distorting the taxonomy.
  • Format normalization in the prompt — state format expectations (ISO 8601 dates, decimal currency) alongside the schema, since the schema alone cannot enforce them.
Tip

Making every field required does not improve completeness — it pushes the model to fabricate values. Optional and nullable fields are how you get honest 'not present' answers.

How the exam will try to trick you

The distractors below look right under time pressure — learn the tell.

  1. The trap

    Assume tool_use with a JSON schema prevents all extraction errors.

    Correct answer

    tool_use eliminates JSON syntax errors only — semantic errors (wrong sums, misplaced or fabricated values) still slip through.

    Why: A schema constrains structure, not the correctness of the values placed in it.

  2. The trap

    Use tool_choice: "auto" when you need guaranteed structured output.

    Correct answer

    Use "any" (some tool) or a named tool to force a call; reserve "auto" for when a plain text reply is acceptable.

    Why: "auto" lets the model answer in text instead of calling a tool, so structured output is not guaranteed.

  3. The trap

    Make every schema field required to guarantee complete extraction.

    Correct answer

    Make fields optional or nullable when the source may omit them.

    Why: Required fields pressure the model to fabricate plausible values for information that is genuinely absent.

Key takeaways

  • Tool use with a JSON schema guarantees schema-shaped output and eliminates JSON syntax errors.
  • tool_choice 'auto' allows text; 'any' forces some tool; a named tool forces exactly that one.
  • Schemas stop syntax errors but never semantic errors like wrong sums or misplaced values.
  • Make fields optional/nullable so Claude reports absence instead of fabricating values.
  • Add 'unclear' and 'other + detail' options for ambiguous or edge-case categoricals.
  • Put format rules (ISO dates, decimal currency) in the prompt; the schema can't enforce them.

Frequently asked questions

How do you force JSON output from Claude?+

Define a tool whose input_schema is your target JSON shape and set tool_choice to that named tool. Claude returns a tool_use block conforming to the schema, which eliminates malformed JSON far more reliably than asking for JSON in the prompt.

Does tool use guarantee the extracted values are correct?+

No. Tool use guarantees the structure is valid and parseable, but semantic errors — wrong totals, misplaced values, or fabricated fields — still occur. You need external validation and retry logic to catch those.

Practice makes pass

Ready to test what you just learned?

Reading gets you familiar — answering questions gets you certified. Jump into free practice or sit a full timed mock exam, scored 100–1000 just like the real thing.

No sign-up required · Explanation for every answer · Works offline