Skip to content
News · 8 min read

Structured Outputs Are Now GA on the Claude API

Structured outputs have moved from beta to GA on the Claude API. Covers what changed, how JSON schema validation works, practical examples with tool_use and direct schema enforcement, and what it means for the exam.

AI Cert GuidesPublished Updated
On this page4 sections

Anthropic has moved structured outputs from beta to general availability. If you've been sending the structured-outputs-2025-11-13 beta header to get guaranteed JSON schema conformance, you can drop it. Structured outputs are now a first-class API feature with no beta flag required.

Note: The exam tests structured output patterns (Domain 4) — the move to GA status doesn't change the concepts tested, but it means you no longer need to pass the beta header in production.

What changed from beta to GA

Two things:

  1. No more beta header. The anthropic-beta header is no longer required. Structured output parameters work directly in the standard API.

  2. A new parameter name. The beta output_format parameter has moved to output_config.format. The old name still works if you send the beta header, but it's deprecated and will be removed. Version 1.0 and later of the Python SDK raises a TypeError if you pass output_format to client.beta.messages.create().

Two limits are worth knowing before you design a schema:

  • Supported schema features. You can use anyOf, allOf (though allOf can't contain $ref), enum, const and internal $ref definitions, and every object needs additionalProperties: false. Recursive schemas, external $ref and numeric or string-length constraints such as minimum and maxLength aren't supported.
  • First-request latency. The API compiles each new schema into a grammar that constrains token generation, so the first request with a given schema takes longer. Compiled grammars are cached for 24 hours from their last use.

How structured outputs work

Structured outputs guarantee that Claude's response conforms to a JSON schema you provide. This isn't prompt-based — it's not "please output JSON." The API constrains token generation at the decoding level so the output can't violate your schema, short of a refusal or a max_tokens cut-off. Every key, every type, every required field is enforced.

There are two ways to use this.

Direct JSON schema enforcement

Pass your schema in output_config.format:

python
import json

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "Analyse this customer review and extract sentiment, key topics, and urgency level: 'The product arrived damaged and support hasn't responded in 3 days. Very frustrated.'"
        }
    ],
    output_config={
        "format": {
            "type": "json_schema",
            "schema": {
                "type": "object",
                "properties": {
                    "sentiment": {
                        "type": "string",
                        "enum": ["positive", "negative", "neutral", "mixed"]
                    },
                    "topics": {
                        "type": "array",
                        "items": {"type": "string"}
                    },
                    "urgency": {
                        "type": "string",
                        "enum": ["low", "medium", "high", "critical"]
                    },
                    "summary": {
                        "type": "string"
                    }
                },
                "required": ["sentiment", "topics", "urgency", "summary"],
                "additionalProperties": False
            }
        }
    }
)

if response.stop_reason in ("refusal", "max_tokens"):
    raise RuntimeError(f"Output may not match the schema: stop_reason={response.stop_reason}")
analysis = json.loads(response.content[0].text)

The JSON arrives in the response's text block and matches that schema, unless the response stops for a refusal or at max_tokens, so check stop_reason before you parse it. On a normal stop you'll always get a sentiment from the enum, an array of topics, an urgency level, and a summary. No parsing failures, no missing fields, no creative reinterpretation of your structure.

Structured outputs via tool_use

The second approach uses tool definitions with input_schema. Set "strict": True on the tool and the input Claude generates is constrained to that schema, the same way output_config.format constrains a direct answer. Without strict, the schema guides the input but doesn't guarantee it. Tool definitions have been the standard pattern for structured extraction since the tool_use feature launched.

python
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=[
        {
            "name": "extract_order_details",
            "description": "Extract structured order information from customer messages.",
            "strict": True,
            "input_schema": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "Order number in format ORD-XXXXX"
                    },
                    "issue_type": {
                        "type": "string",
                        "enum": ["refund", "exchange", "tracking", "damage", "other"]
                    },
                    "amount": {
                        "type": "number",
                        "description": "Order amount if mentioned"
                    },
                    "requires_escalation": {
                        "type": "boolean"
                    }
                },
                "required": ["order_id", "issue_type", "requires_escalation"],
                "additionalProperties": False
            }
        }
    ],
    tool_choice={"type": "tool", "name": "extract_order_details"},
    messages=[
        {
            "role": "user",
            "content": "I need a refund for order ORD-44821, it was $129.99 and arrived completely smashed."
        }
    ]
)

Using tool_choice to force a specific tool turns the tool call into a structured extraction step. The model must call the tool, and with strict set the input must conform to the schema. This gives you the same guarantee as output_config.format but routes through the tool_use mechanism.

Current state (checked 29 September 2026): forced tool_choice now depends on the model. Claude Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1 reject {"type": "tool"} and {"type": "any"} with a 400 error, and manual extended thinking rejects both on any model. The example above uses Claude Sonnet 4.6, which isn't on that list. On the newer models, keep tool_choice on auto and set strict: true on the tool, or use structured outputs. The exam guide still tests forced selection, so answer with it on the exam.

Which approach to use

Use output_config.format when you want Claude's direct answer in a structured format. Use tool_use with forced tool choice when you're building the extraction into an agentic workflow where the structured data feeds into subsequent tool calls or agent steps.

In practice, tool_use is more common in production systems because it integrates naturally with the agentic tool loop. output_config.format is cleaner for standalone extraction tasks where you just need structured data back.

Validation still matters

Structured outputs guarantee schema conformance — the JSON will be valid and match your types. They don't guarantee semantic correctness. If you ask Claude to extract an order number and it hallucinates one that looks plausible but doesn't exist, the output will still conform to your schema. It'll be a valid string in the right format. Just wrong.

Always validate extracted values against your source data:

python
# The SDK returns the tool input as a dict, already parsed
tool_input = response.content[0].input

# Schema conformance is guaranteed — but verify the data
order = db.lookup_order(tool_input["order_id"])
if not order:
    # The model extracted a plausible but incorrect order ID
    handle_extraction_error(tool_input)

This is a critical distinction the exam tests. Schema enforcement and semantic validation are separate concerns. Structured outputs handle the first. Your application code handles the second.

What this means for the exam

Domain 4 tests structured output patterns — specifically how to use JSON schemas with the API, when to use tool_use for structured extraction, and how to validate outputs. The move from beta to GA doesn't change any of these concepts. The patterns are identical. You drop the beta header and pass the schema in output_config.format.

The exam-relevant concepts are:

  • Schema design: choosing the right types, enums for constrained values, required vs optional fields
  • tool_use as structured extraction: forcing tool calls with tool_choice to guarantee structured output
  • Validation layering: understanding that schema conformance and semantic correctness are different things
  • When structured outputs break down: very large schemas with deep nesting can increase latency; extremely open-ended tasks don't benefit from rigid schemas

Work through Domain 4 — Prompt Engineering & Structured Output for the full treatment, or jump to Task 4.3 — Structured Output for the specific task statement.

CCAR-F · Domain 4Study Prompt Engineering & Structured OutputFree lessons, written for the exam.Start studying →

Keep reading