Claude API JSON Mode: Structured Outputs Guide
If you're searching for a "JSON mode" toggle in the Claude API like the one OpenAI offers, the short answer is: it doesn't exist as a single flag. Anthropic's Messages API has no response_format: json_object parameter. What it does have — and what actually gets you reliable structured output — is tool use (function calling), combined with a well-defined JSON schema and, optionally, response prefilling.
This matters because the goal behind "JSON mode" searches is almost always the same: you want Claude to return data you can JSON.parse() without regex cleanup, markdown fences, or "Sure, here's the JSON you asked for:" preambles. Claude can do this reliably — you just need to use the right mechanism instead of a magic flag.
Why Claude skips the dedicated JSON mode
Anthropic's design choice leans on tool use as the general-purpose structured output mechanism. Instead of a separate mode that constrains the whole response, you define a tool with an input_schema (JSON Schema), and Claude populates that schema when it decides to "call" the tool. Force the model to always use that tool, and you effectively get guaranteed structured JSON — with the added benefit that you also get strict typing, nested objects, enums, and required fields, which a bare JSON-mode flag usually doesn't give you.
Method 1: Tool use with a forced tool choice (recommended)
This is the most reliable way to get structured JSON from Claude today. Define a tool whose schema matches the shape you want, then set tool_choice to force Claude to use it.
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" },
"line_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["description", "amount"]
}
}
},
"required": ["vendor", "total", "line_items"]
}
}],
"tool_choice": { "type": "tool", "name": "extract_invoice" },
"messages": [
{ "role": "user", "content": "Invoice from Acme Corp, total $420, items: Widget $300, Shipping $120" }
]
}'
The response contains a tool_use content block with an input object that matches your schema exactly — no stray text, no code fences to strip. This is the closest Claude gets to a guaranteed JSON contract, and it's the pattern most production integrations use for extraction, classification, routing, and form-filling tasks.
Method 2: System prompt + prefill
If you don't need strict schema enforcement, you can get Claude to output plain JSON by combining clear instructions with response prefilling — seeding the assistant's reply so it has no room to add a preamble:
{
"messages": [
{ "role": "user", "content": "Return a JSON object with keys \"name\" and \"age\" for: John, 34 years old." },
{ "role": "assistant", "content": "{" }
]
}
By prefilling the assistant turn with {, you remove the model's tendency to say "Here's the JSON:" first. You still need to parse the result defensively (prepend the { back, validate with a schema library) because this method has no hard schema enforcement — it relies on instruction-following, which is good but not airtight.
Method 3: Validate and retry
Whichever method you use, treat the output as untrusted until it passes validation:
- Parse with
JSON.parse()(or your language's equivalent) inside a try/catch. - Validate the parsed object against a schema (Zod, Pydantic,
ajv, etc.). - On failure, send the invalid output back to Claude with a short correction message: "Your last response was invalid JSON. Return only valid JSON matching the schema."
- Cap retries at 2–3 attempts to avoid runaway token usage.
Tool use with a forced tool_choice makes this retry loop almost unnecessary for simple schemas, but for deeply nested or conditional structures, a validate-and-retry step is still good defensive engineering.
Structured outputs with SubToAPI
If you're already calling Claude through SubToAPI, the tool-use pattern above works the same way against the SubToAPI endpoint — you just swap the host and auth header:
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": [ /* same tool schema as above */ ],
"tool_choice": { "type": "tool", "name": "extract_invoice" },
"messages": [ { "role": "user", "content": "..." } ]
}'
SubToAPI exposes the same tools, tool_choice, and streaming semantics documented at /docs/tools, /docs/messages, and /docs/streaming, so structured-output patterns you build and test against the direct Claude API transfer over without rewriting your extraction logic. The extra value is in the surrounding infrastructure: per-application API keys (sub_live_...), usage metadata per request, and team seats — useful once your structured-output pipeline moves from a prototype into something multiple services or team members depend on. Check /pricing for plan details, or start a free trial at /signup.
Best practices checklist
- Use tool use with a forced
tool_choicewhen you need guaranteed schema conformance. - Use prefilling for lightweight, low-stakes JSON needs where strict enforcement isn't worth the extra setup.
- Keep schemas flat where possible — deeply nested optional fields increase the chance of malformed output.
- Always validate parsed JSON against a schema library before trusting it downstream.
- Log raw model output during development so you can diagnose schema drift early.
- Set
max_tokenshigh enough that Claude isn't truncated mid-object, which is a common cause of "invalid JSON" errors.
Questions
Does Claude have a response_format: json_object parameter like OpenAI? No. Claude has no equivalent flag. The supported way to get structured output is tool use with a defined input_schema, optionally forced with tool_choice.
What's the most reliable way to guarantee valid JSON from Claude? Define a tool with a JSON Schema and set tool_choice to force that specific tool. Claude populates the schema in the tool_use block, giving you type-checked, parseable output without extra text.
Can I still get plain JSON without using tools? Yes, with clear system instructions and response prefilling (seeding the assistant turn with {), but you should validate the result yourself since there's no hard schema enforcement, unlike the tool-use approach.