Structured Outputs Basics
10 examples to get you started with Structured Outputs - 7 basic and 3 intermediate.
Prerequisites
- Install the official SDK:
pip install anthropic. - Set your API key in the environment:
export ANTHROPIC_API_KEY=sk-ant-.... - Optional but recommended for schema definitions:
pip install pydantic. - Every example below assumes
from anthropic import Anthropicandclient = Anthropic()unless shown otherwise.
Basic Examples
1. A Minimal Schema-Constrained Request
The smallest possible output_config.format call - one string field, guaranteed back as valid JSON.
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {"greeting": {"type": "string"}},
"required": ["greeting"],
"additionalProperties": False,
},
}
},
messages=[{"role": "user", "content": "Say hello in French."}],
)
print(response.content[0].text)output_config.formatis what enforces the shape - the prompt itself needs no "return JSON" instruction.requiredandadditionalProperties: Falseare required parts of a well-formed schema, not optional extras.- The response text is guaranteed to be valid JSON matching this schema, but it is still a string - you still call
json.loads()on it here.
2. Using client.messages.parse Instead of create
Skip the manual json.loads() step by using the SDK's parsing helper.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {
"language": {"type": "string"},
"greeting": {"type": "string"},
},
"required": ["language", "greeting"],
"additionalProperties": False,
}
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Say hello in French."}],
)
print(response.parsed_output)client.messages.parsevalidates the response against your schema and hands back a parsed value onresponse.parsed_output.- No
json.loads()call is needed anywhere in this snippet. - This is the recommended entry point for most structured-output workloads in Python.
3. Defining the Schema with a Pydantic Model
Use Pydantic to define the target shape once, and reuse it for both the schema and the parsed result type.
from pydantic import BaseModel
from anthropic import Anthropic
class Greeting(BaseModel):
language: str
greeting: str
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={
"format": {"type": "json_schema", "schema": Greeting.model_json_schema()}
},
messages=[{"role": "user", "content": "Say hello in French."}],
)
greeting: Greeting = response.parsed_output
print(greeting.language, greeting.greeting)Greeting.model_json_schema()generates the JSON Schema from the Pydantic model, so you don't hand-write it twice.response.parsed_outputhere is a realGreetinginstance, not a plain dict.- This pairing (Pydantic model +
parse) is the most common pattern for structured extraction in Python.
4. Requesting an Array of Objects
Schemas aren't limited to a single flat object - request a list of structured items in one call.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {
"todos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"task": {"type": "string"},
"done": {"type": "boolean"},
},
"required": ["task", "done"],
"additionalProperties": False,
},
}
},
"required": ["todos"],
"additionalProperties": False,
}
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Give me 3 sample todo items."}],
)
for item in response.parsed_output["todos"]:
print(item["task"], item["done"])- The top-level response is still a single JSON object - wrap arrays in a named field rather than returning a bare array.
- Every nested object needs its own
requiredandadditionalProperties: False, not just the outer one. - This pattern is common for "give me N items" style extraction tasks.
5. Using an Enum to Constrain a Field's Values
Restrict a field to a fixed set of values instead of accepting any string.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {
"sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
},
"required": ["sentiment"],
"additionalProperties": False,
}
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Classify: 'This product exceeded my expectations!'"}],
)
print(response.parsed_output["sentiment"])enumguarantees the returned value is one of the listed strings - no post-hoc validation of free text needed.- This is the standard pattern for classification-style responses.
- Enums are one of the well-supported constructs - check the field types reference before relying on less common schema keywords.
6. Checking stop_reason Before Trusting the Output
Always confirm the response actually finished before treating it as complete JSON.
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=256,
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {"summary": {"type": "string"}},
"required": ["summary"],
"additionalProperties": False,
},
}
},
messages=[{"role": "user", "content": "Summarize the plot of a 300-page novel in detail."}],
)
if response.stop_reason == "max_tokens":
print("Response was truncated - retry with a larger max_tokens.")
else:
print(response.content[0].text)stop_reason == "max_tokens"means the model ran out of room before finishing - the JSON may be cut off mid-structure.- A schema guarantee only holds for a response that actually completed generating.
- Checking
stop_reasonbefore parsing is cheap insurance against a confusingJSONDecodeErrordownstream.
7. Setting a Generous max_tokens Up Front
Reduce truncation risk for larger structured responses by sizing max_tokens to the expected output.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {
"bullet_points": {"type": "array", "items": {"type": "string"}},
},
"required": ["bullet_points"],
"additionalProperties": False,
}
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=4096, # generous headroom for a multi-item array response
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "List 10 detailed release notes for a v2.0 launch."}],
)
print(response.parsed_output["bullet_points"])- Larger or more numerous fields need more
max_tokensheadroom than a single short string. - A
max_tokensthat's too tight is the most common cause of truncated structured output in practice. - This is a starting heuristic, not a guarantee - pair it with the
stop_reasoncheck from Example 6 in production code.
Intermediate Examples
8. A Multi-Field Extraction Schema with Nested Objects
Combine multiple field types, including a nested object, in a single extraction schema.
from pydantic import BaseModel
from anthropic import Anthropic
class Address(BaseModel):
city: str
country: str
class Contact(BaseModel):
name: str
email: str
is_customer: bool
address: Address
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-5",
max_tokens=1024,
output_config={"format": {"type": "json_schema", "schema": Contact.model_json_schema()}},
messages=[{
"role": "user",
"content": (
"Extract contact info: Jane Doe, jane@example.com, existing "
"customer, lives in Austin, USA."
),
}],
)
contact: Contact = response.parsed_output
print(contact.name, contact.address.city)- Pydantic models can nest inside each other, and
model_json_schema()flattens that into$ref/$defentries the API understands. response.parsed_outputreturns a fully nested, typedContactobject - including the nestedAddress.- Nested schemas are supported, but very deep or recursive nesting is not - keep structures shallow for reliability.
9. Retrying on Truncation with a Larger Token Limit
Detect a truncated structured response and automatically retry with more room.
from anthropic import Anthropic
client = Anthropic()
schema = {
"type": "object",
"properties": {"report": {"type": "string"}},
"required": ["report"],
"additionalProperties": False,
}
def get_report(max_tokens: int = 512) -> str:
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=max_tokens,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Write a detailed incident report."}],
)
if response.stop_reason == "max_tokens" and max_tokens < 4096:
return get_report(max_tokens=max_tokens * 2)
return response.content[0].text
print(get_report())- The retry doubles
max_tokenseach time, bounded by a ceiling, rather than retrying forever. - This pattern belongs anywhere a structured response's size is hard to predict in advance.
- See the dedicated truncation-handling page for a more complete, production-ready version of this pattern.
10. Structured Output Combined with a Strict Tool Definition
Pair output_config.format with strict: true on a tool so both the final response and any tool call parameters are guaranteed valid.
from anthropic import Anthropic
client = Anthropic()
tools = [{
"name": "log_ticket",
"description": "Log a support ticket in the tracking system.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"priority": {"type": "string", "enum": ["low", "medium", "high"]},
"summary": {"type": "string"},
},
"required": ["priority", "summary"],
"additionalProperties": False,
},
}]
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Server is down, customers can't check out. Log this."}],
)
for block in response.content:
if block.type == "tool_use":
print(block.input) # guaranteed to match the strict input_schemastrict: Trueon a tool definition is the tool-use equivalent ofoutput_config.formaton a message response.- Both features can be used in the same request when a workflow needs a validated final answer and validated tool parameters.
strictschemas follow the samerequired/additionalProperties: falserules asoutput_config.formatschemas.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.