Claude API JSON Mode: How to Get Structured Output
Does Claude have a JSON mode?
Not in the way OpenAI's API defines it. There's no response_format: { type: "json_object" } parameter you can pass to Claude that forces every token to be valid JSON. If you're searching for "claude api json mode structured output," the direct answer is: you get structured output from Claude through tool use (function calling) with a JSON schema, or through careful prompting plus validation on your side — not through a dedicated mode flag.
The good news is that both approaches work reliably in production, and one of them (tool use) is actually more robust than OpenAI's JSON mode because it enforces a schema, not just "valid JSON syntax." Below are the three practical patterns, when to use each, and how to make them bulletproof.
Method 1: Tool use with a strict schema (recommended)
The most reliable way to get structured output from Claude is to define a tool with an input schema and force Claude to call it. Claude will return arguments that match your schema, which you parse directly — no regex, no "strip the markdown fences" hacks.
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 invoice data",
"input_schema": {
"type": "object",
"properties": {
"vendor": { "type": "string" },
"total": { "type": "number" },
"currency": { "type": "string" },
"line_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["description", "amount"]
}
}
},
"required": ["vendor", "total", "currency"]
}
}],
"tool_choice": { "type": "tool", "name": "extract_invoice" },
"messages": [{ "role": "user", "content": "Invoice from Acme Corp, total $450.00, items: consulting $400, shipping $50" }]
}'
Claude's response includes a tool_use content block with an input object that matches your schema exactly:
{
"type": "tool_use",
"name": "extract_invoice",
"input": {
"vendor": "Acme Corp",
"total": 450.0,
"currency": "USD",
"line_items": [
{ "description": "consulting", "amount": 400 },
{ "description": "shipping", "amount": 50 }
]
}
}
Forcing tool_choice to a specific tool is the key step — without it, Claude might just respond in plain text instead of calling the tool. This pattern works for any extraction, classification, or form-filling task where you know the target shape in advance.
Method 2: System prompt + output prefill
For simpler cases, or when you don't want the overhead of defining a tool, you can instruct Claude directly and seed the start of its response:
{
"model": "claude-sonnet-4-5",
"max_tokens": 500,
"system": "You only output valid JSON matching this schema: {\"sentiment\": \"positive|negative|neutral\", \"confidence\": number, \"keywords\": string[]}. No prose, no markdown fences.",
"messages": [
{ "role": "user", "content": "Review: 'This product changed how I work, though the setup was a pain.'" },
{ "role": "assistant", "content": "{" }
]
}
Prefilling the assistant turn with { nudges Claude to continue directly into JSON rather than opening with an explanation. This is lighter-weight than tool use but less strict — Claude can still occasionally wrap output in a code block or add trailing commentary, especially on longer generations. Always validate before trusting it.
Method 3: Explicit schema in the prompt with validation
If you need maximum control over formatting (field order, specific enums, nested structures), put the JSON schema directly in your system prompt and pair it with a validation step in your code:
const schema = {
type: "object",
required: ["title", "priority", "tags"],
properties: {
title: { type: "string" },
priority: { type: "string", enum: ["low", "medium", "high"] },
tags: { type: "array", items: { type: "string" } }
}
};
function validate(output, schema) {
// use a library like ajv in real code
return typeof output.title === "string" &&
["low", "medium", "high"].includes(output.priority) &&
Array.isArray(output.tags);
}
Run the validator after parsing. If it fails, retry with a correction message like "Your previous output was invalid: missing 'priority'. Return corrected JSON only." Claude is good at self-correcting when you feed the exact error back.
Making structured output production-ready
A few things matter regardless of which method you pick:
- Always
JSON.parse()defensively. Wrap in try/catch and have a retry path. - Keep schemas as flat as possible. Deeply nested or highly conditional schemas increase error rates across any LLM, not just Claude.
- Use
max_tokensgenerously enough that Claude doesn't get cut off mid-object — a truncated JSON response is the most common cause of parse failures. - Log the raw response during development so you can see exactly what came back when validation fails.
If you're building this into an application that other services or team members call, you'll also want consistent request logging, usage metadata, and API keys scoped per app rather than sharing one raw Anthropic key. That's the layer SubToAPI adds on top of your Claude access — it exposes a standard HTTPS API with sub_live_... keys, so your extraction service, your internal tool, and your teammates' scripts all hit the same endpoint with separate keys and separate usage tracking. The underlying messages endpoint and tool use support work exactly like the patterns above, so none of your schema or validation code changes.
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [{ "name": "extract_invoice", "input_schema": { "...": "..." } }],
"tool_choice": { "type": "tool", "name": "extract_invoice" },
"messages": [{ "role": "user", "content": "..." }]
}'
Check the quickstart if you want to wire this up in a few minutes, or see pricing for plan details — there's a free trial at signup if you want to test the tool-use pattern against your own schemas first.
questions
Does Claude support a response_format parameter like OpenAI's JSON mode? No. Claude has no equivalent flag. Structured output is achieved through tool use with an input schema, or through prompting plus prefill and validation.
Which method gives the most reliable structured output? Tool use with tool_choice forced to a specific tool. It enforces an actual schema rather than just JSON syntax, so field names, types, and required properties are far less likely to drift.
What's the most common cause of broken JSON from Claude? Truncation from a max_tokens limit set too low, followed by the model wrapping output in markdown code fences when no explicit "no formatting" instruction is given.