Claude Academy
Sign in

Vault / wiki/201/structured-output.md

updated 2026-05-28

Structured Output

How to make Claude return reliable, machine-parseable output.

Three techniques, ranked

  1. Tool-use with tool_choice = {type: "tool", name: "..."} — the most reliable. The tool's input_schema is your JSON schema. Claude is forced to emit a tool_use block matching it.
  2. Prefill the assistant turn — start with { to force JSON, or <answer> for an XML tag.
  3. Prompt-only constraints — say "Return JSON matching this schema" plus a clear example. Cheapest, least reliable.

Forced tool pattern (recommended)

extract_tool = {
    "name": "extract_invoice",
    "description": "Extract structured fields from an invoice.",
    "input_schema": {
        "type": "object",
        "properties": {
            "vendor": {"type": "string"},
            "total": {"type": "number"},
            "currency": {"type": "string", "enum": ["USD", "EUR", "GBP"]},
            "line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "amount": {"type": "number"},
                    },
                    "required": ["description", "amount"],
                },
            },
        },
        "required": ["vendor", "total", "currency", "line_items"],
    },
}

resp = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=[extract_tool],
    tool_choice={"type": "tool", "name": "extract_invoice"},
    messages=[{"role": "user", "content": invoice_text}],
)

data = resp.content[0].input  # already a dict matching schema

Prefill pattern

messages = [
    {"role": "user", "content": "..."},
    {"role": "assistant", "content": "{"},  # prefill
]
# Claude continues from `{`. Concatenate "{" + response.

Schema design tips

  • Use enums when fields have a fixed vocabulary.
  • Mark nullable fields explicitly with "type": ["string", "null"].
  • Require what truly must be present — overdoing required causes hallucination.
  • Add descriptions to every property — they guide Claude's choices.
  • Avoid deep nesting — flatten when you can.

Validation loops

Always validate after generation. Pattern:

1. Generate.
2. Run JSON schema validation (jsonschema, pydantic).
3. If invalid: re-prompt with the validator's error message included.
4. Cap retries (~3) and route to human review if still failing.

Provenance

For extraction, ask Claude to include source pointers alongside the value:

{"vendor": "ACME", "vendor_source": "page 1, line 2"}

This lets you spot-check or audit. CCA-F frequently tests "how do you make extractions auditable?" — provenance is the answer.

See also