← Blog

Claude API Structured Output: JSON Mode Guide

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

Does Claude API have a JSON mode?

No, the Claude API does not have a dedicated "JSON mode" flag the way some other providers do. There is no response_format: json_object parameter you can set. Instead, Anthropic recommends — and the ecosystem has converged on — using tool use (function calling) to force structured output, combined with careful prompting for cases where you want raw JSON in the text response instead.

This article covers both approaches: the tool-based method (the reliable one) and the prompt-based method (the simpler one), plus how to validate and parse the result safely in production.

Why structured output matters

If you're building anything that feeds Claude's response into another system — a database insert, a form, a UI component, another API call — you need predictable, parseable output. Free-text responses from an LLM will occasionally include markdown fences, explanatory sentences before the JSON, or inconsistent key names. Structured output techniques exist to eliminate that variance.

Method 1: Use tool definitions to force a schema

The most reliable way to get structured JSON from Claude is to define a tool with an input schema and force Claude to call it. Claude will then return a tool_use content block whose input field is a JSON object matching your schema — no parsing of free text required.

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-20250514",
    "max_tokens": 1024,
    "tools": [{
      "name": "extract_invoice",
      "description": "Extract structured invoice data",
      "input_schema": {
        "type": "object",
        "properties": {
          "invoice_number": {"type": "string"},
          "total": {"type": "number"},
          "due_date": {"type": "string"},
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": {"type": "string"},
                "amount": {"type": "number"}
              },
              "required": ["description", "amount"]
            }
          }
        },
        "required": ["invoice_number", "total"]
      }
    }],
    "tool_choice": {"type": "tool", "name": "extract_invoice"},
    "messages": [
      {"role": "user", "content": "Invoice #4421, total $920.00, due March 3. Items: Consulting $800, Travel $120."}
    ]
  }'

The key part is tool_choice. Setting it to {"type": "tool", "name": "..."} forces Claude to call that specific tool rather than deciding on its own whether to use it. The response contains a tool_use block:

{
  "type": "tool_use",
  "name": "extract_invoice",
  "input": {
    "invoice_number": "4421",
    "total": 920.0,
    "due_date": "March 3",
    "line_items": [
      {"description": "Consulting", "amount": 800},
      {"description": "Travel", "amount": 120}
    ]
  }
}

That input object is already parsed JSON matching your schema — no string parsing, no stray text. This is the closest thing to "JSON mode" that the Claude API offers, and it's actually more robust because the schema is enforced at the API level rather than hoped for in the prompt.

For deeper detail on schema design, nested objects, enums, and multi-tool setups, see /docs/tools.

Method 2: Prompt-based JSON output

Sometimes defining a full tool schema is overkill — for example, a one-off script or a simple extraction task. In that case, you can ask Claude directly for JSON in the system prompt and reinforce it in the user message:

{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 512,
  "system": "You output only valid JSON. Do not include markdown code fences, explanations, or any text outside the JSON object.",
  "messages": [
    {"role": "user", "content": "Classify this review as positive, negative, or neutral and give a one-sentence reason. Review: 'Shipping took forever but the product itself is great.' Return JSON with keys 'sentiment' and 'reason'."}
  ]
}

This works reasonably well with Claude but is less reliable than tool use for two reasons:

A trick that improves reliability: prefill the assistant's response with { using the assistant role in your messages array. This nudges Claude to continue directly into JSON without preamble.

{
  "messages": [
    {"role": "user", "content": "..."},
    {"role": "assistant", "content": "{"}
  ]
}

Anthropic's models respect this prefill, and you just prepend the { back when parsing the output.

Validating the output

Regardless of method, always validate before trusting the result in production:

import Ajv from "ajv";

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

const parsed = typeof toolInput === "string" ? JSON.parse(toolInput) : toolInput;

if (!validate(parsed)) {
  console.error(validate.errors);
  // retry with a corrective message, or fall back to a default
}

Wrap parsing in try/catch for the prompt-based method, since Claude occasionally emits malformed JSON even with instructions. Tool-based input is far less prone to this since Anthropic validates the structure against your schema before returning it.

Handling retries

For production pipelines, build in a retry loop that appends the validation error back into the conversation:

if (!validate(parsed)) {
  messages.push({
    role: "user",
    content: `That JSON didn't match the schema: ${ajv.errorsText(validate.errors)}. Please fix and resend.`
  });
  // make another request
}

This is rarely needed with tool use but is a good safety net for the prompt-based approach or for schemas with unusual constraints (regex patterns, strict enums, etc.).

Keeping it simple with a proxy

If you're calling Claude from multiple services or languages, you often end up rewriting the same tool-definition and parsing boilerplate repeatedly. SubToAPI exposes your Claude access as a standard HTTPS API at api.subtoapi.app/v1/messages, using the same request and response shapes documented here, so your structured-output code — tool schemas, tool_choice, prefill tricks — works unchanged. You get an sub_live_... key, streaming support, and usage metadata per request without managing provider credentials directly. See /docs/messages and /docs/tools for the exact request format, or /docs/quickstart to get a key in a few minutes. Plans start at €9/month, with a free trial at /signup.

Questions

Does Claude API support response_format: json_object like OpenAI? No. Claude has no equivalent parameter. The supported way to force structured output is tool use with a forced tool_choice, which returns a parsed JSON object matching your schema.

Is tool use always better than prompting for JSON? For anything you need to parse reliably, yes. Tool use enforces structure at the API level. Prompting is fine for low-stakes, simple outputs, but it occasionally returns malformed or wrapped JSON.

Can I get streaming structured output? Yes, tool use works with streaming — you'll receive incremental input_json_delta events that you accumulate and parse once the tool_use block is complete. See /docs/streaming for details on handling streamed events.

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 →