← Blog

Claude API Function Calling for Reliable JSON Output

2026-10-05 · 4 min read · SubToAPI Team

If you need the Claude API to return structured, predictable JSON instead of free-form prose, the answer is tool use (Claude's version of function calling). You define a JSON schema for the data you want back, pass it to the API as a "tool," and Claude responds with a tool_use block containing arguments that match your schema — not a paragraph you have to parse with regex.

This is the pattern to use any time you're extracting data, filling a form, classifying content, or feeding Claude's output into another system. Prompting Claude to "respond only in JSON" works most of the time, but it's fragile: markdown fences, stray commentary, or a missing comma can break your parser. Tool use removes that ambiguity because the model is constrained to produce arguments that conform to a schema you control.

Why prompting for JSON isn't enough

A plain prompt like "return the result as JSON" relies on the model following instructions perfectly, every time, across every input. In practice you'll see:

Tool use fixes this at the API level. You describe the exact shape of the output as a JSON Schema, Claude calls the "tool" with arguments matching that shape, and you parse tool_use.input directly — no string cleanup required.

Defining a tool for structured output

A tool definition has a name, a description, and an input_schema using standard JSON Schema. Here's a tool for extracting structured data from a support ticket:

{
  "name": "extract_ticket_info",
  "description": "Extract structured fields from a customer support message",
  "input_schema": {
    "type": "object",
    "properties": {
      "summary": { "type": "string" },
      "category": {
        "type": "string",
        "enum": ["billing", "bug", "feature_request", "account", "other"]
      },
      "priority": {
        "type": "string",
        "enum": ["low", "medium", "high", "urgent"]
      },
      "requires_followup": { "type": "boolean" }
    },
    "required": ["summary", "category", "priority", "requires_followup"]
  }
}

By listing fields in required and using enum for closed sets of values, you narrow the model's choices and make downstream parsing deterministic.

Example: calling the API

Here's the full request/response cycle using curl. SubToAPI exposes the same Messages and tool-use interface as the native Claude API, so this works whether you're calling Claude directly or routing through a SubToAPI key:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4",
    "max_tokens": 512,
    "tools": [{
      "name": "extract_ticket_info",
      "description": "Extract structured fields from a customer support message",
      "input_schema": {
        "type": "object",
        "properties": {
          "summary": { "type": "string" },
          "category": { "type": "string", "enum": ["billing", "bug", "feature_request", "account", "other"] },
          "priority": { "type": "string", "enum": ["low", "medium", "high", "urgent"] },
          "requires_followup": { "type": "boolean" }
        },
        "required": ["summary", "category", "priority", "requires_followup"]
      }
    }],
    "tool_choice": { "type": "tool", "name": "extract_ticket_info" },
    "messages": [{
      "role": "user",
      "content": "My card was charged twice this month and I need a refund ASAP."
    }]
  }'

Setting tool_choice to force a specific tool is the key detail here — it tells Claude it must call extract_ticket_info rather than just answering in plain text. The response contains a tool_use content block:

{
  "type": "tool_use",
  "name": "extract_ticket_info",
  "input": {
    "summary": "Customer charged twice this month, requesting a refund",
    "category": "billing",
    "priority": "high",
    "requires_followup": true
  }
}

That input object is already valid JSON matching your schema — no parsing tricks needed.

Parsing the response in JavaScript

const res = 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",
    max_tokens: 512,
    tools: [ticketTool],
    tool_choice: { type: "tool", name: "extract_ticket_info" },
    messages: [{ role: "user", content: ticketText }],
  }),
});

const data = await res.json();
const toolCall = data.content.find((c) => c.type === "tool_use");
const ticket = toolCall.input; // typed object, ready to use

No JSON.parse on model text, no try/catch for malformed output — the structure is enforced by the schema contract.

Tips for reliable structured extraction

If you're running multiple extraction tools or chaining tool calls together, the deeper mechanics of parallel and sequential tool use are worth understanding — see /docs/tools for the full reference. For general request/response structure, /docs/messages covers the Messages API shape, and /docs/quickstart walks through authentication and your first call end to end.

Questions

Does tool use guarantee perfectly valid JSON every time? It guarantees output conforming to your JSON Schema's structure far more reliably than prompt-based instructions, but you should still validate the response against your schema before using it in production, especially for complex nested objects.

Can I use function calling just for JSON output without actually calling a function? Yes — this is a common pattern. You define a tool purely to describe the shape of data you want, set tool_choice to force that tool, and never actually execute anything. The "function" is really just a schema contract.

What's the difference between tool use and Claude's native JSON mode-style prompting? Prompt-based JSON relies on instruction-following and can produce malformed or decorated output. Tool use enforces a schema at the API level, returning a parsed tool_use.input object instead of a text string you need to clean and parse yourself.

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 →