Claude structured output: get valid JSON with output_config, strict tools and schemas
Claude's structured outputs make a response follow your JSON Schema through constrained decoding: set output_config.format to a json_schema and the reply is valid, parseable JSON that matches it. Use strict tool use (strict: true) when the JSON is a tool's input instead. Two limits remain: a refusal or a max_tokens cutoff can break the shape, and schema-valid data can still be wrong.
What are Claude structured outputs?
Structured outputs "constrain Claude's responses to follow a specific schema, ensuring valid, parseable output for downstream processing". They work through constrained decoding: the model can only produce tokens that keep the output valid against your schema. That is a guarantee, not a hope, which is what separates it from asking nicely for JSON in the prompt.
There are two features, and you can use them together:
| Feature | How you turn it on | What it guarantees |
|---|---|---|
| JSON outputs | output_config.format with type: "json_schema" |
Claude's answer is JSON that matches your schema |
| Strict tool use | strict: true on a tool definition |
Tool names and inputs match their schemas |
In the docs' words: JSON outputs control what Claude says; strict tool use validates how Claude calls your functions.
Claude JSON output with output_config.format
Here a damage report is extracted from a customer email at a bike-rental shop:
json
{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Extract the damage report from this email: ..."}],
"output_config": {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"booking_id": {"type": "string"},
"part": {"type": "string", "enum": ["brakes", "chain", "wheel", "battery", "frame", "other"]},
"safety_issue": {"type": "boolean"},
"customer_quote": {"type": "string"}
},
"required": ["booking_id", "part", "safety_issue", "customer_quote"],
"additionalProperties": false
}
}
}
}
The JSON arrives in the response's text content block. The client SDKs add helpers that build the schema from Pydantic or Zod types and parse the result for you.
Strict tool use: JSON as a tool's input
Before JSON outputs existed, the classic way to get JSON was a tool whose input_schema is your format: Claude "calls" it and the input is your data. That pattern still works, and strict: true now guarantees the input matches the schema. It is the natural choice when the JSON is an action's arguments rather than the final answer, and it is how you guarantee valid tool calls on the models where forcing a tool with tool_choice is no longer accepted. More on tools in LLM tool calling with Claude.
JSON Schema rules for structured outputs
| Supported | Not supported |
|---|---|
| object, array, string, integer, number, boolean, null | Recursive schemas, external $ref |
enum, const, anyOf, allOf, $ref with $defs |
minimum, maximum, multipleOf |
required, default, some string formats |
minLength, maxLength |
additionalProperties: false (required on objects) |
Array constraints beyond minItems 0 or 1 |
An unsupported feature returns a 400 error with details. Two practical notes: enum and const capitalization is not guaranteed, so compare them case-insensitively; and the constraints you cannot express (a positive amount, a maximum length) belong in your own validation.
When structured output can still fail
- Refusal. The response has
stop_reason: "refusal"(HTTP 200, billed), and the output may not match the schema because the refusal takes precedence. - max_tokens. The output may be cut off and invalid; retry with a higher limit.
Always check stop_reason before parsing; the Claude API basics list every value.
Valid JSON, wrong data: validate the content
A schema guarantees shape, not truth. A damage report can be perfectly valid and still name the wrong booking, or invent a value for a field the email never mentioned. Three habits help:
- Make fields nullable when the source may not contain them, so the model can say "not stated" instead of guessing.
- Check the meaning in code: the booking exists, amounts add up, dates make sense.
- Retry with the error: when a check fails, send the specific validation error back and ask for a correction. A retry helps when the information is in the source; it cannot help when the information was never there.
Examples of tricky documents in the prompt, as described in few-shot prompting, also cut empty and invented fields.
What changed: output_format is deprecated
The output_format parameter moved to output_config.format, and beta headers are no longer required. output_format is deprecated and only works with the structured-outputs-2025-11-13 beta header. Older tutorials still use it.
Structured output on the Claude Certified Architect exam
Domain 4 is 20% of the CCAR-F exam. Task 4.3 expects structured output through tool use and JSON schemas, schemas with optional and nullable fields that prevent made-up values, and task 4.4 the difference between format errors (fixed by the schema) and semantic errors (caught by your checks and a retry with the error). Scenario 6, structured data extraction, is built on exactly this.
More in all articles.
Sources: Structured outputs; Strict tool use; Define tools; Claude Certified Architect Foundations exam guide v1.0, task statements 4.3 and 4.4.
FAQ
How do I get JSON output from Claude?
Use structured outputs: add output_config with format type json_schema and your schema. Claude returns JSON matching the schema in the response's text block. Alternatively, define a tool whose input schema is your format, with strict set to true.
What is the difference between JSON outputs and strict tool use?
JSON outputs control the format of Claude's answer. Strict tool use validates the name and inputs of tool calls. You can use either, or both in one request.
Can structured output still fail?
Yes. On a refusal or when the reply hits max_tokens, the output may not match the schema. And the schema guarantees structure, not that the values are right, so validate the content in your code.
Is output_format still supported?
It is deprecated. The parameter moved to output_config.format and needs no beta header; output_format only works with the structured-outputs-2025-11-13 beta header.