Skip to content
Bedrock now forces Claude's JSON to match your schema instead of hoping it parses Split diagram. Left side, without structured outputs: a request to Claude returns freeform text, a parser attempt fails, and the flow loops back into a retry. Right side, with structured outputs on Amazon Bedrock: the same request carries a JSON schema, and Claude returns one schema-compliant response with no retry loop, labeled GA October 20, 2026. Claude's JSON now has to fit the box Bedrock structured outputs, GA Oct 20, 2026, on every Claude model WITHOUT STRUCTURED OUTPUTS Prompt: "Return the ticket as JSON" claude response (free text) Sure, here's the ticket: { category: billing ...priority "high" (missing closing brace) JSON.parse() throws retry logic, custom parser, still flaky WITH STRUCTURED OUTPUTS Request + JSON schema (output_config.format) claude response (schema-valid) { "category": "billing", "priority": "high", "escalate": true, "sentiment": "negative" } Guaranteed match. No retry. bedrock-runtime + bedrock-mantle Claude Sonnet, Opus, Haiku, Fable, Mythos families . per AWS Bedrock docs
Guideintermediate

Bedrock structured outputs for Claude: stop parsing broken JSON

Published October 21, 2026 · by Pondero Labs

The short version

Amazon Bedrock now returns schema-validated JSON from every Claude model, on bedrock-runtime and bedrock-mantle. Here is the request shape, the limits, and when to use it over the direct Anthropic API.

Table of Contents

Bedrock structured outputs for Claude: stop parsing broken JSON

Ask Claude for JSON and you will eventually get a response that almost parses. A trailing comma, a markdown code fence Claude added out of habit, a field that is a string in one run and a number in the next. We have shipped retry wrappers around exactly this problem more than once. As of October 20, 2026, Amazon Bedrock closes that gap for Claude: every supported model now returns JSON that is validated against a schema you define, on both the bedrock-runtime and bedrock-mantle endpoints, per AWS. No retry logic. No post-processing pass to coerce types.

This guide covers what the feature actually guarantees, which models and endpoints carry it, the request shape for both mechanisms Bedrock exposes, where it still breaks, and when to reach for Bedrock's version instead of calling Claude directly through Anthropic's own API.

What changed, mechanically

Bedrock gives Claude two related but distinct guarantees, documented in the Bedrock structured outputs reference:

  • JSON Schema output format. You pass a schema; Claude's entire text response conforms to it. Use this for extraction, report generation, and API responses.
  • Strict tool use. Add "strict": true to a tool definition and the tool's arguments are validated against its input_schema at generation time, not after the fact. Use this for agent tool calls and multi-step automation.

They stack. A single request can define a response schema and a strict tool in the same call.

Under the hood, Bedrock validates your schema against a supported subset of JSON Schema Draft 2020-12 the first time it sees it. If the schema uses an unsupported feature, you get a 400 error immediately, not a failed generation three steps into a pipeline. New schemas compile into a constrained grammar, which the Bedrock docs say can take "up to a few minutes" on first use; compiled grammars are then cached for 24 hours, so repeat requests with the same schema run at normal inference latency. That first-compile tax is the one planning detail worth building into a deploy checklist: warm the schema before traffic hits it, not during a launch.

AWS What's New post dated October 20, 2026 announcing structured outputs for all Anthropic Claude models on both the bedrock-runtime and bedrock-mantle endpoints Source: aws.amazon.com, captured October 21, 2026.

Which models and endpoints actually support it

Support spans the Claude model lineup, but it is not uniform. Per the Bedrock docs, structured outputs is available "across the Claude Sonnet, Opus, Haiku, Fable, and Mythos model families," and support varies by model and endpoint: some models land on bedrock-runtime only, some on bedrock-mantle only, many on both. On bedrock-runtime you can reach it through the Converse API, the InvokeModel API, or the Anthropic Messages API. On bedrock-mantle, which AWS designed for agentic workloads with server-side tools and stateful conversations, you use the Anthropic Messages API directly. Check the specific model's card before you build against it; the docs also flag that some geographic cross-Region inference profiles drop support for specific models (Claude Haiku 4.5 through the India inference profile is the example AWS calls out), so a model that works in us-east-1 is not guaranteed to work identically through every routing profile.

How to turn it on

On bedrock-runtime, if you are calling the InvokeModel API with the Anthropic-native request body, the schema goes directly into output_config.format as a JSON object:

import boto3
import json

bedrock_runtime = boto3.client("bedrock-runtime", region_name="us-east-1")

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string", "description": "Customer name"},
        "email": {"type": "string", "description": "Customer email address"},
        "plan_interest": {"type": "string", "description": "Product plan of interest"},
        "demo_requested": {"type": "boolean", "description": "Whether a demo was requested"},
    },
    "required": ["name", "email", "plan_interest", "demo_requested"],
    "additionalProperties": False,
}

response = bedrock_runtime.invoke_model(
    modelId="global.anthropic.claude-sonnet-5-5",
    body=json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 512,
        "messages": [{"role": "user", "content": (
            "Paulo Santos ([email protected]) is interested in our Enterprise "
            "plan and wants to schedule a demo for next Tuesday at 2pm."
        )}],
        "output_config": {"format": {"type": "json_schema", "schema": schema}},
    }),
)
body = json.loads(response["body"].read())
if body["stop_reason"] == "end_turn":
    result_text = next(b["text"] for b in body["content"] if b["type"] == "text")
    print(json.dumps(json.loads(result_text), indent=2))

(Converse API users get the same mechanism through outputConfig.textFormat instead; the shape is in the Bedrock docs.)

On bedrock-mantle, where agentic tool calling lives, strict tool use is the mechanism:

from anthropic import AnthropicBedrockMantle

client = AnthropicBedrockMantle(aws_region="us-east-1")

message = client.messages.create(
    model="anthropic.claude-sonnet-5",
    max_tokens=512,
    messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}],
    tools=[{
        "name": "get_weather",
        "description": "Get the current weather for a specified location",
        "strict": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "The city and state, e.g., San Francisco, CA"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["location", "unit"],
            "additionalProperties": False,
        },
    }],
)
for block in message.content:
    if block.type == "tool_use":
        print(f"Tool: {block.name}  Input: {block.input}")

With strict: true set, location is always a string and unit is always celsius or fahrenheit; the model cannot add a field the schema does not define. Both examples follow the request shapes documented in the Bedrock structured outputs reference.

Three paths to the same guarantee

The hard part isn't the feature, it's picking which surface to build against. AWS's own Supported APIs or features table lists Converse, ConverseStream, InvokeModel, cross-Region inference, batch inference, and the Messages API on bedrock-mantle, all as supported paths, but they sit on top of different SDKs with different ergonomics.

PathAPI surfaceSchema mechanismBest for
bedrock-runtimeConverse, InvokeModel, ConverseStreamoutput_config.format / outputConfig.textFormat, raw JSON SchemaTeams already on boto3, single-turn extraction jobs, batch inference pipelines
bedrock-mantleAnthropic Messages APIstrict: true tool definitionsAgentic workflows that need server-side tools and want to stay inside an AWS account for IAM and billing
Direct Anthropic APIMessages API (native)client.messages.parse() with a Pydantic or type-based output_format, or a raw schema in output_config.formatA typed object in code with SDK-managed schema translation; no AWS account or IAM setup required

The direct Anthropic API path is worth calling out because it is not the same developer experience. Anthropic's SDKs ship a helper, client.messages.parse(), that takes a Pydantic model (or an equivalent typed object in other languages) and does the schema translation for you: it strips unsupported constraints like minimum or maxLength, folds them into the field's description instead, adds additionalProperties: false automatically, and then validates the response against your original schema, constraints included, in your own code, per Anthropic's docs. On Bedrock's bedrock-runtime path, you build the simplified schema yourself, by hand, in output_config.format. That is not a limitation so much as a different point on the build-it-yourself-versus-managed spectrum, and it is the detail that should drive your pick more than the headline feature parity does.

Where it still breaks

Structured outputs on Bedrock is a constrained subset of JSON Schema, not the full spec, and the gaps are specific enough to design around rather than discover in production:

  • No recursive schemas and no external $ref references (internal $ref/$def/definitions are fine).
  • No numerical constraints: minimum, maximum, and multipleOf are unsupported.
  • No string length constraints: minLength and maxLength are unsupported.
  • additionalProperties has to be false. Anything else is rejected.
  • It is incompatible with citations. Turn on citations for an Anthropic model while structured outputs is active and Bedrock returns a 400 error, per the Bedrock docs.

Amazon Bedrock docs listing supported and unsupported JSON Schema Draft 2020-12 features for structured outputs, including the additionalProperties:false requirement and unsupported recursive schemas Source: docs.aws.amazon.com, captured October 21, 2026.

Practically, that ruling-out list means the usual validation work does not disappear, it moves. A field like confidence_score still needs a minimum: 0, maximum: 1 check somewhere; Bedrock just will not enforce it for you, so you add a thin validation step after you get the (type-correct, field-complete) JSON back. The schema still does the heavy lifting: it rules out missing fields, a string where you expected a boolean, and a stray markdown fence wrapped around the object.

Where this actually earns its keep

AWS points at financial-document extraction, parsing clinical notes into EHR records, contract analysis for key terms and obligations, product catalog enrichment, and support-ticket routing, where intent, sentiment, and category always need to match a fixed downstream schema. The pattern across all five: you are feeding a system that expects a strict data model (a CRM field, a database column, a routing table) and the old failure mode was Claude being "close enough" instead of exactly right.

Ticket classification is the cleanest version of it, because the schema doubles as the business logic:

schema = {
    "type": "object",
    "properties": {
        "category": {"type": "string", "enum": ["billing", "technical", "account", "sales", "other"]},
        "priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]},
        "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "escalate": {"type": "boolean"},
        "reason": {"type": "string", "description": "One-sentence justification"},
    },
    "required": ["category", "priority", "sentiment", "escalate", "reason"],
    "additionalProperties": False,
}

Run a lightweight model like Claude Haiku 5.5 against that schema and escalate is always a real boolean you can branch on in code, never a string like "true" that silently passes an if check it shouldn't. The AWS example of this pattern uses Haiku specifically because classification and routing are high-volume, repetitive workloads where a smaller model with a guaranteed-valid output beats a larger model you still have to double-check.

The verdict

If your pipeline already lives inside AWS (IAM roles, CloudTrail, an existing bedrock-runtime or bedrock-mantle integration), use Bedrock's structured outputs and stop writing retry wrappers around Claude's JSON. Pick bedrock-runtime with output_config.format for single-turn extraction jobs bolted onto an existing boto3 pipeline; pick bedrock-mantle with strict tool use for agent workflows that need server-side tool calls and want to stay inside your AWS billing and audit perimeter.

If you are starting from zero, outside AWS, and want the schema translation and response validation handled for you in a couple of lines, call Claude through the direct Anthropic API and use client.messages.parse() with a Pydantic model. You get the same schema-compliance guarantee, per Anthropic's docs, with less schema plumbing to maintain. Either way, the retry-loop-around-broken-JSON pattern that has been standard practice since function calling first shipped is now optional, not load-bearing.