← Blog

Claude API JSON Mode Output Formatting Guide

2026-10-12 · 5 min read · SubToAPI Team

Claude does not have a dedicated response_format: json parameter like some other model APIs. If you're searching for "claude api json mode output formatting" because you want guaranteed, parseable JSON back from every request, the short answer is: use tool use (function calling) with a strict input schema, not a toggle. This is the closest thing Claude has to a JSON mode, and it works more reliably than prompting alone.

The second most reliable approach, when tool use isn't a fit, is a combination of explicit system instructions, a strict output schema in the prompt, and response prefilling — seeding the start of Claude's reply with { so it can't wander into prose before the JSON starts. Below is how to implement both, plus the common failure modes and how to avoid them.

Why there's no "JSON mode" toggle

Some APIs expose a literal mode flag that forces the model to only emit valid JSON. Claude's API doesn't expose that flag directly. Instead, Anthropic's recommended pattern is to treat structured output as a tool call: you define a tool with an input schema, instruct Claude to always use that tool, and Claude returns a tool_use block whose input field is already a parsed JSON object matching your schema — no regex extraction, no trailing explanation text to strip out.

This is actually more robust than a generic JSON mode because the schema is enforced at the field level (types, required keys, enums), not just "is this valid JSON."

Method 1: Tool use as JSON mode

Define a single tool that represents your desired output shape, and tell Claude it must call 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": {
          "invoice_number": {"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": ["invoice_number", "total", "currency"]
      }
    }],
    "tool_choice": {"type": "tool", "name": "extract_invoice"},
    "messages": [{"role": "user", "content": "Invoice #4482, total $340.00, items: hosting $200, support $140"}]
  }'

The tool_choice field forces Claude to call that exact tool instead of answering in free text. The response contains a tool_use content block with an input object that already conforms to your schema — this is your JSON, ready to pass straight into JSON.parse or your deserializer.

If you're calling Claude through SubToAPI, the same pattern applies against https://api.subtoapi.app/v1/messages with your sub_live_ key — tool definitions and tool_choice are passed through unchanged, and the response shape matches the standard Messages format. See /docs/tools and /docs/messages for the full parameter reference.

Method 2: Prompting + prefill

When a full tool schema is overkill — say you just need a quick structured reply for an internal script — a lighter approach works:

  1. State the exact schema in the system prompt, including types and key order.
  2. Tell Claude explicitly: "Respond with JSON only. No markdown, no explanation, no code fences."
  3. Prefill the assistant turn with { so the model continues the object rather than starting a new sentence.
const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "content-type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-5",
    max_tokens: 512,
    system: "Output only valid JSON matching: {\"sentiment\": \"positive\"|\"negative\"|\"neutral\", \"confidence\": number}. No other text.",
    messages: [
      { role: "user", content: "This product completely changed how our team works." },
      { role: "assistant", content: "{" }
    ]
  })
});

Prefilling with { is the key trick: Claude's reply continues from that character, so there's no leading "Here's the JSON you requested:" to strip out before parsing. You still need to prepend the { back onto the response text before parsing, since it's part of the prefill rather than the generated output.

Common formatting failures and fixes

For high-stakes extraction pipelines, tool use with tool_choice beats prompt-based JSON every time because the schema violation gets caught structurally rather than discovered after a failed parse in production.

Where SubToAPI fits

If you're building an internal tool or product feature on top of Claude and want JSON-structured responses without juggling raw API keys per environment, SubToAPI gives you an HTTPS endpoint with sub_live_ application keys, request logging, and per-key usage metadata — useful when you need to debug why a particular call returned malformed JSON three environments away from your laptop. Plans start at Solo (€9) with a free trial at /signup; see /pricing for Team and Scale tiers, or jump straight to /docs/quickstart to wire up your first structured request.

questions

Does Claude have an official JSON mode like response_format: json? No. Anthropic recommends tool use with a strict input_schema and tool_choice set to force a specific tool — this produces reliably parseable structured output without a dedicated mode flag.

Why does Claude sometimes wrap JSON in markdown code fences? By default, Claude treats JSON as code and formats it accordingly. Explicitly instruct it not to use code fences, or use tool use, which returns the input field as a native object with no fencing at all.

What's the most reliable way to guarantee valid JSON every time? Tool use with tool_choice: {"type": "tool", "name": "..."}. It enforces types and required fields at the API level, unlike prompt-based instructions which can drift on edge cases or long conversations.

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 →