← Blog

Claude API JSON Mode Output: A Practical Example

2026-09-23 · 4 min read · SubToAPI Team

If you're looking for a json_mode: true flag like OpenAI's API, Claude doesn't have one. There is no single parameter that forces structured output. What Claude does have is a more reliable mechanism: tool use (function calling) with a JSON schema, which in practice produces cleaner, more consistent JSON than OpenAI's JSON mode does, because the model is constrained by a schema instead of just told "output JSON."

This article shows the actual pattern developers use to get predictable JSON from Claude, with a full working example, plus the fallback prompting technique for cases where you don't want to use tools at all.

Why Claude has no "JSON mode" toggle

Anthropic's API doesn't expose a response_format parameter. Instead, structured output is achieved through the tools parameter — you define a tool with an input_schema, tell Claude it must use that tool, and Claude returns a tool_use block whose input field is already a parsed JSON object matching your schema. This is more robust than free-text JSON because:

Getting JSON output with tool use

Here's a minimal example that extracts structured data from a support ticket.

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_ticket",
        "description": "Extract structured fields from a support ticket",
        "input_schema": {
          "type": "object",
          "properties": {
            "priority": { "type": "string", "enum": ["low", "medium", "high", "urgent"] },
            "category": { "type": "string" },
            "summary": { "type": "string" },
            "requires_refund": { "type": "boolean" }
          },
          "required": ["priority", "category", "summary", "requires_refund"]
        }
      }
    ],
    "tool_choice": { "type": "tool", "name": "extract_ticket" },
    "messages": [
      { "role": "user", "content": "My order arrived broken and I want my money back ASAP, this is the second time this month." }
    ]
  }'

The key detail is tool_choice: { "type": "tool", "name": "extract_ticket" }. This forces Claude to call that specific tool instead of replying in plain text, which is what actually guarantees you get JSON back every time.

The response contains a content array with a tool_use block:

{
  "type": "tool_use",
  "name": "extract_ticket",
  "input": {
    "priority": "urgent",
    "category": "shipping_damage",
    "summary": "Item arrived broken, second occurrence this month, customer wants refund.",
    "requires_refund": true
  }
}

input is already a JSON object — no regex, no markdown fence stripping, no retry loop for malformed JSON.

The prompting fallback (no tools)

If you don't want to use tools — for example you're building a quick script and don't need schema validation — you can still get reasonably reliable JSON by being explicit in the prompt and constraining the response format:

Respond with valid JSON only, no markdown formatting, no explanation.
Match this exact structure:
{"priority": string, "category": string, "summary": string, "requires_refund": boolean}

This works most of the time but is meaningfully less reliable than tool use — Claude can occasionally wrap the output in a code fence or add a sentence before the JSON, especially on longer conversations. For production pipelines that parse the output programmatically, tool-based extraction is the safer default.

Doing the same thing through SubToAPI

If you're calling Claude through SubToAPI, the request shape is identical — SubToAPI proxies the Messages API 1:1, so tool use, tool_choice, and structured input schemas work exactly as documented above. You just swap the endpoint 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": [ ... ],
    "tool_choice": { "type": "tool", "name": "extract_ticket" },
    "messages": [ ... ]
  }'

This is useful if your team is already running production traffic through SubToAPI for the usage dashboard, team seats, and per-key rate limits — you don't lose any structured-output capability by going through it. Full parameter reference is in the docs, and the tool-calling specifics are covered in /docs/tools. If you're new to the API, /docs/quickstart walks through the first request end to end.

Validating the output in your code

Even with tool use, it's good practice to validate the returned object against your schema before trusting it downstream — model behavior can still drift on edge cases (ambiguous enums, missing optional fields).

import Ajv from "ajv";

const ajv = new Ajv();
const validate = ajv.compile(schema);

const toolUseBlock = response.content.find(b => b.type === "tool_use");
const valid = validate(toolUseBlock.input);

if (!valid) {
  console.error(validate.errors);
  // retry, or fall back to a stricter prompt
}

This costs almost nothing and catches the rare malformed response before it hits your database or downstream service.

Summary

FAQ

Does Claude support a response_format: json parameter like OpenAI? No. Anthropic's API has no equivalent parameter. The reliable way to get structured JSON is tool use with a forced tool_choice.

Is tool-based JSON output slower or more expensive than plain text? Marginally — the schema adds a small number of input tokens, and output is typically similar length. The reliability gain almost always outweighs the cost.

Can I get JSON output while streaming? Yes, tool use works with streaming, but you need to accumulate the input_json_delta events into a complete JSON string before parsing. See /docs/streaming for the event format.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →