Free playbooks in your inbox
Hands-on tutorials for people who want to build with AI.

Claude Structured Outputs: Guaranteed-Valid JSON

Claude structured outputs guarantee schema-valid JSON with constrained decoding. When to enforce vs instruct, the parameter, and the one thing it can't fix.

From the youcanbuildthings catalog ▸ Build-tested 7 min read

Summary:

  1. Claude structured outputs guarantee schema-valid JSON through constrained decoding.
  2. Enforce with a system when the output must hold; instruct with a prompt when it is advisory.
  3. The raw-API parameter is output_config.format; no parse-retry loop is needed.
  4. The guarantee covers the shape, not the value, so validate a wrong value against real data.

Claude structured outputs are the feature that ends the “please return valid JSON” prompt for good. Instead of asking the model nicely and bolting a try-except around the parse, you constrain the decoding so invalid JSON is impossible. That is the whole shift, and it is the second judgment call the Claude Certified Architect exam tests in Domain 3, Prompt Engineering and Structured Output, worth 20% of your score: do you ask the model, or do you make the system guarantee it?

Should you instruct or enforce?

You enforce when being wrong is expensive, and you instruct when it is not. That single question decides most of this domain. If the output is advisory, a summary, a draft, a suggestion, a prompt instruction is fine. If it feeds a downstream parser, touches money or compliance, or has to be machine-readable without fail, you enforce it with a system.

Reach for a prompt instructionReach for a system (schema / tool / hook)
Advisory outputMust be valid JSON
Tone and styleFeeds a downstream parser
One-off formattingMoney or compliance fields
Exploratory draftsMust hold every time

Decision table titled instruct or enforce, with a left column reach for a prompt instruction listing advisory output, tone and style, one-off formatting, exploratory drafts, and a right column reach for a system listing must be valid JSON, feeds a downstream parser, money or compliance fields, must hold every time, footer prompts suggest systems enforce

Prompts suggest; systems enforce. Writing “always return valid JSON” and hoping is the instinct that fails, because a prompt is a suggestion the model usually follows, not a wall it cannot cross.

How do structured outputs work?

Structured outputs use constrained decoding to guarantee the model emits JSON that matches your schema. Not “usually matches.” Matches. The model literally cannot produce tokens that would violate it. Here is what the docs promise, verbatim:

Structured outputs guarantee schema-compliant responses through constrained decoding.

  • Always valid: no more JSON.parse() errors
  • Type safe: guaranteed field types and required fields
  • Reliable: no retries needed for schema violations

Source: Claude API docs, structured outputs. There are two ways to reach it, and keeping them straight is a known trap:

On the raw API, the parameter is output_config.format:

# Raw API: the parameter is output_config.format
response = client.messages.create(
    model=MODEL, max_tokens=1024, messages=messages,
    output_config={"format": {"type": "json_schema", "schema": my_schema}},
)

In the Python SDK, messages.parse takes output_format= and hands back a parsed object:

# Python SDK helper: messages.parse returns a typed object
response = client.messages.parse(
    model=MODEL, max_tokens=1024, messages=messages,
    output_format=MyPydanticModel,
)
data = response.parsed_output   # already a typed object

The raw parameter is output_config.format; the Python helper kwarg is output_format= and hands back parsed_output. They look close enough that a careless answer swaps them. The old beta top-level output_format moved to output_config.format, so on a raw-API call, output_config.format is the current one. And because the JSON is guaranteed valid, the defensive parse-retry loop people wrap around it is dead code. There are no schema violations to catch.

Add it in three steps

  1. Define the schema you actually need, with additionalProperties: false on every object, and only the fields the downstream consumer reads.
  2. Pass it through output_config.format on the raw API, or hand a Pydantic model to messages.parse(output_format=...) in the SDK.
  3. Delete the try-except JSON-repair loop. Handle the two real exceptions instead: a refusal or a max_tokens cutoff can end a turn before the JSON is complete, so branch on those, not on malformed JSON.

What broke: valid JSON, wrong value

Here is the failure that catches people who think the guarantee covers everything. A structured output can be perfectly schema-valid and completely wrong. The guarantee covers the shape of the JSON. It says nothing about whether the value is correct.

# Schema-valid is not correct. Constrained decoding guarantees the SHAPE, never the value.
extracted        = {"invoice_total": 4200.00}   # valid JSON, correct type
source_of_truth  = {"invoice_total": 2400.00}   # what the paper invoice says

schema_ok   = isinstance(extracted["invoice_total"], float)
semantic_ok = extracted["invoice_total"] == source_of_truth["invoice_total"]

print("schema valid: ", schema_ok)
print("value correct:", semantic_ok)

Run it and the shape passes while the value fails:

schema valid:  True
value correct: False

The model returned 4200.00 when the real total was 2400. Schema-perfect, value-garbage. The tempting fix is “tighten the schema,” and it is the wrong instinct, because the schema was never the problem. A wrong value is a semantic failure, and the fix is semantic validation: assert the extracted value against the real source, a database, the document, a checksum, something that is only true when the value is right. Match the remedy to the failure. Invalid shape wants a schema; a wrong value wants a check against reality.

One more tool-side fact rides along: when a tool keeps receiving malformed arguments, strict: true guarantees the arguments match the tool’s schema, the same guarantee pointed at tool inputs instead of a final answer. Begging the model in the prompt to format its arguments is the workaround it replaces.

What should you actually do?

  • If the output must be valid JSON every time → enforce with structured outputs; delete the parse-retry loop.
  • If the output is advisory or stylistic → instruct in the prompt; a schema is overkill.
  • If a tool keeps getting malformed arguments → set strict: true on the tool.
  • If a structured output is valid JSON but the value is wrong → validate against real data; do not tighten the schema.
  • If you need a numeric range enforced → do it in code; constrained decoding guarantees types, not value ranges.

The bottom line

  • The guarantee is real. Constrained decoding makes invalid JSON impossible, so the retry loop and the JSON-repair helper are solving a problem that no longer exists.
  • Enforce or instruct is the whole domain. Must-hold output gets a system; advisory output gets a prompt. Reaching for a prompt to guarantee structure is the bait.
  • Schema is not semantics. Valid JSON with a wrong value is the trap that survives every schema tweak, and the only fix is checking the value against something real.
  • Know the parameter. output_config.format on the raw API, messages.parse(output_format=) in the SDK. Swapping them is the careless miss.
Why trust this? Every youcanbuildthings guide is pulled from a build-tested book: code that ran in production before it was written down.

Frequently Asked Questions

What are Claude structured outputs?+

A feature that uses constrained decoding to guarantee Claude's response is valid JSON matching your schema. The model cannot emit tokens that violate the schema, so you get always-valid, type-safe output with no parse-retry loop.

When should you enforce output instead of instructing the prompt?+

Enforce when the output must be valid JSON, feeds a downstream parser, touches money or compliance, or must hold every time. Instruct with a prompt for advisory output, tone, one-off formatting, and exploratory drafts.

Do structured outputs guarantee the values are correct?+

No. They guarantee the shape and types of the JSON, not the truth of the values. A schema-valid response can still hold a wrong number; that needs semantic validation against real data, not a tighter schema.