Anthropic — Structured outputs (output_config.format, strict tools)
Status: DOCUMENTED · LIVE_VERIFIED (claude-haiku-4-5-20251001, 8 calls). GA on the Claude API since 2025-12-04 (no beta header; structured-outputs-2025-11-13 and top-level output_format were transition-period only — live: output_format now returns 400).
Sources: Structured outputs · Strict tool use · Messages API · Release notes
Last verified: 2026-09-18
Request
{"model": "claude-haiku-4-5-20251001", "max_tokens": 100,
"output_config": {"format": {"type": "json_schema", "schema": {
"type": "object",
"properties": {"name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"}},
"required": ["name", "age", "city"], "additionalProperties": false}}},
"messages": [{"role": "user", "content": "Ada is 36 and lives in Lyon. Extract the person."}]}Response: a single text block containing valid JSON — live {"name":"Ada","age":36,"city":"Lyon"} (205 input / 16 output tokens). Strict tools: tools[].strict: true → grammar-constrained tool_use.input (live with tool_choice: {type: tool} → {"name":"Ada","age":36,"city":"Lyon"}; the block also carried caller: {"type": "direct"}).
SDK helpers: Python client.messages.parse(output_format=PydanticModel) (→ parsed_output, still accepts output_format as convenience), TypeScript zodOutputFormat() / jsonSchemaOutputFormat(), Java outputConfig(Class<T>), Ruby output_config: {format: Model}. Python/TS/Ruby/PHP SDKs transform unsupported constraints (strip minimum, etc., add additionalProperties: false, move constraints into descriptions, validate locally).
Supported models
All active models: Fable 5.1/5, Mythos 5.1/5/Preview, Opus 5, 4.8, 4.7, 4.6, 4.5, Sonnet 5, 4.6, 4.5, Haiku 4.5 (Models API capabilities.structured_outputs.supported: true for all 11 listed ids). Bedrock: Opus 4.6, Sonnet 4.6, Sonnet 4.5, Opus 4.5, Haiku 4.5.
JSON Schema subset
| Supported | Not supported (→ 400 with details) |
|---|---|
| types object, array, string, integer, number, boolean, null | recursive schemas |
enum (strings/numbers/bools/nulls) — casing of returned enum/const values not guaranteed |
complex types inside enum |
const |
external $ref (http://…) |
anyOf, allOf (not allOf + $ref) |
numeric constraints minimum/maximum/multipleOf — live: 400 output_config.format.schema: For 'integer' type, property 'minimum' is not supported |
$ref, $defs, definitions (internal) |
string constraints minLength/maxLength |
default |
array constraints other than minItems 0/1 |
required, additionalProperties: false (objects must set it) |
additionalProperties ≠ false; patternProperties — live: 400 … For 'object' type, property 'patternProperties' is not supported |
formats date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid |
other formats |
minItems 0 or 1 |
|
pattern: ^…$, * + ? {n,m} (simple), classes [] . \d \w \s, groups |
backreferences, lookaround, \b, large {n,m} |
Complexity limits: 20 strict tools/request; 24 optional parameters total; 16 union-typed parameters (anyOf, type arrays); internal grammar size limit → 400 Schema is too complex for compilation; compile timeout 180 s.
Behaviour
- Property order: required properties first (schema order), then optional ones.
- Grammar compilation: first use of a schema adds latency (live: 2.2 s first call vs 0.9 s streaming re-use; strict tool first call 14.7 s); compiled grammars cached 24 h since last use; cache keyed on schema structure + tool set (not
name/description). - Prompt: an extra system prompt is injected (more input tokens); changing
output_config.formatinvalidates the prompt cache. Grammar applies only to the final text output, not to tool calls/results or thinking. - Invalid outputs:
stop_reason: "refusal"(200, may not match schema),stop_reason: "max_tokens"(truncated JSON → raisemax_tokens), enum casing drift. - Data: ZDR for prompts/responses, but the schema itself is cached 24 h — never put PHI in schema names/enums/patterns.
Compatibility
| Feature | Result |
|---|---|
| Streaming | ✔ JSON arrives as ordinary text_delta chunks (live) — accumulate then parse |
| Token counting | ✔ count_tokens accepts output_config.format without compiling (live: 205 tokens, same as the real call) |
| Batches | ✔ 50 % discount |
| Strict tools + JSON output together | ✔ |
| Thinking | ✔ grammar state resets between thinking and text |
| Citations | ✘ 400 Citations cannot be enabled when output format is set. Please disable citations on uploaded document blocks. (live) |
| Assistant prefill | ✘ |
max_tokens: 0 pre-warm |
✘ |
On-demand compaction |
✘ (rejected on summarize requests) |
Legacy output_format |
✘ 400 output_format: This field is deprecated. Use 'output_config.format' instead. (live) |
Examples & tests
examples/anthropic/structured-output/ (sh/py/ts) · tests/anthropic/test_structured_output.py.