Claude API JSON Mode: Structured Data Extraction
Developers searching for "Claude API JSON mode" are usually looking for a way to force Claude to return clean, parseable JSON instead of prose wrapped around a code block. The short answer: Claude does not have a dedicated response_format: json flag like some other APIs, but it has something more reliable for structured data extraction — tool use. You define a JSON schema as a tool, Claude calls that tool with arguments matching the schema, and you get back validated, typed JSON every time.
There's also a simpler fallback for quick scripts: prefilling the assistant's response with { and using a strict system prompt. Both approaches work. This article covers when to use each, with working examples.
Why Claude doesn't need a "JSON mode"
Other APIs added JSON mode because free-form text generation kept producing malformed output — trailing commentary, markdown fences, inconsistent key names. Claude's tool use feature solves the same problem more robustly: instead of asking the model to describe JSON in text, you give it a function signature and it fills in typed arguments. The model is trained specifically to produce well-formed tool calls, so you avoid the parsing fragility of "please only output JSON" prompts.
If you're doing structured data extraction — pulling fields out of invoices, resumes, support tickets, emails, log lines — tool use is the right tool for the job, not a workaround.
Method 1: Tool use for structured extraction
Define a tool whose input schema matches the structure you want back. Claude will return a tool_use content block with arguments that conform to that schema.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [{
"name": "extract_invoice",
"description": "Extract structured fields from an invoice",
"input_schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"vendor": {"type": "string"},
"total_amount": {"type": "number"},
"currency": {"type": "string"},
"line_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {"type": "string"},
"quantity": {"type": "number"},
"unit_price": {"type": "number"}
},
"required": ["description", "quantity", "unit_price"]
}
}
},
"required": ["invoice_number", "vendor", "total_amount", "currency"]
}
}],
"tool_choice": {"type": "tool", "name": "extract_invoice"},
"messages": [
{"role": "user", "content": "Invoice #4421 from Acme Supplies, 3x Widget @ 12.50 EUR, 2x Gadget @ 40.00 EUR. Total: 117.50 EUR"}
]
}'
Setting tool_choice to force a specific tool means Claude always responds with that tool call instead of plain text — this is the closest equivalent to "JSON mode" in the Claude API. The response arrives as a tool_use block:
{
"type": "tool_use",
"name": "extract_invoice",
"input": {
"invoice_number": "4421",
"vendor": "Acme Supplies",
"total_amount": 117.50,
"currency": "EUR",
"line_items": [
{"description": "Widget", "quantity": 3, "unit_price": 12.50},
{"description": "Gadget", "quantity": 2, "unit_price": 40.00}
]
}
}
Parse input directly — no regex stripping of markdown fences, no retry loop for malformed JSON. For a deeper walkthrough of defining tool schemas, see /docs/tools.
Method 2: Prefilling for lightweight extraction
If a full tool schema feels like overkill — a single-field extraction, a quick classification task — you can prefill the assistant turn with { to nudge Claude straight into JSON output without any preamble:
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 256,
system: "Extract the sentiment and topic from the user message. Respond with JSON only: {\"sentiment\": string, \"topic\": string}",
messages: [
{ role: "user", content: "This new update is slow and buggy, really disappointed." },
{ role: "assistant", content: "{" }
]
})
});
Claude continues from { and produces "sentiment": "negative", "topic": "product update"}. Prepend the { back on when you join the strings, run it through JSON.parse, and you're done. This is faster to set up than a tool schema but gives you no built-in validation — malformed output is still possible on edge cases, so wrap the parse in a try/catch and retry on failure.
Which method to pick
- Tool use: nested objects, arrays, multiple required fields, anything you'll validate against a schema downstream. More setup, far more reliable.
- Prefilling: single-purpose scripts, prototypes, low-stakes classification where an occasional parse failure is acceptable.
For anything running in production — invoice processing, lead qualification, log parsing pipelines — use tool use. The schema acts as documentation and validation in one place, and you avoid writing custom JSON-repair logic.
Handling extraction at scale
Structured extraction jobs tend to run in batches: thousands of documents, each needing a tool call. A few practical notes:
- Keep
max_tokenstight relative to your schema size — large free-text fields inside a schema will eat your budget faster than expected. - Log the raw
tool_useinput alongside your parsed result during development; it's the fastest way to spot schema drift when documents are messier than your test set. - If you're building this behind an internal service rather than calling Anthropic directly, routing requests through an API key wrapper keeps logging and rate limiting centralized. SubToAPI turns your existing Claude access into a standard HTTPS API with application keys (
sub_live_...), so your extraction service callshttps://api.subtoapi.app/v1/messagesthe same way it would call Anthropic directly, with tool use and streaming both supported — see /docs/messages and /docs/tools for request shapes.
Either way, the request format for tool-based extraction stays identical: define the schema once, force the tool choice, parse input.
A note on reliability
Tool use schemas aren't a hard guarantee against every edge case — if a document has genuinely ambiguous data, Claude may leave a field null or make a reasonable guess rather than fail outright. Mark fields required only when they're truly always present in your input data, and handle null or missing optional fields in your downstream code rather than assuming a perfect schema match every time.
Questions
Does Claude have a response_format: json parameter like OpenAI? No. Claude's equivalent for guaranteed structured output is tool use with a forced tool_choice, which returns schema-validated arguments instead of free text.
Can I use regular prompting instead of tools to get JSON? Yes, with a system prompt asking for JSON-only output and assistant prefilling with {. It works for simple cases but is less reliable than tool use for nested or multi-field schemas.
Is tool-based extraction slower than plain text generation? Not meaningfully — the model still generates tokens at the same rate, it's just generating structured arguments instead of prose. Keep max_tokens sized to your schema to control cost and latency, covered in /docs/messages.