
Stop Parsing Markdown: Use Claude's Structured Outputs for Schema-Valid JSON Every Time
Chris Harper
2 min read
Jul 15, 2026 · 20:08 UTC
TL;DR: Prompting Claude for JSON works ~70% of the time; structured outputs use schema-constrained decoding to make it work 99.9%+ of the time — no parsing retries, no markdown-wrapped surprises.
Asking Claude to "respond with JSON only" fails in the edge cases that matter most: markdown wrapping, extra commentary, mismatched keys. Two built-in features constrain generation at the token level and eliminate the failure mode.
Pattern 1: output_format for direct JSON responses
The Python SDK's .messages.parse() accepts a Pydantic model and returns a .parsed_output that's already deserialized and validated:
from pydantic import BaseModel
from anthropic import Anthropic
class Ticket(BaseModel):
title: str
priority: str # "low" | "medium" | "high"
labels: list[str]
client = Anthropic()
resp = client.messages.parse(
model="claude-haiku-4-5",
max_tokens=512,
messages=[{"role": "user", "content": "Triage: login button broken on Safari."}],
output_format=Ticket,
)
print(resp.parsed_output)
# Ticket(title='Login button broken on Safari', priority='medium', labels=['bug', 'safari'])
No json.loads(), no try/except, no retry loop. The SDK strips the code block, parses, validates.
Pattern 2: strict: true on tool definitions
When Claude calls your functions (the tool-use loop), strict: true guarantees the input block exactly matches your schema — no extra fields, no missing required fields:
tools = [{
"name": "create_ticket",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"priority": {"type": "string", "enum": ["low", "medium", "high"]},
},
"required": ["title", "priority"],
"additionalProperties": False, # required for strict mode
}
}]
Key tips
- Always set
additionalProperties: false— strict mode requires it on every object - Schema caching: the first request with a new schema compiles a grammar (a few hundred ms of extra latency); subsequent requests with the same schema are cached for 24 hours
- Cost: use
claude-haiku-4-5for flat schemas with ≤10 fields — near-identical accuracy at a fraction of Sonnet's cost - Refusals:
stop_reason: "refusal"can still occur; check before accessing.parsed_output - Both patterns compose: use
output_formatfor Claude's answer ANDstrict: trueon tools in the same agentic loop
Sources: Structured outputs — Claude Platform Docs · Strict tool use — Claude Platform Docs · Hands-on with Anthropic's structured output capabilities — Towards Data Science