← Blog

Claude Function Calling: A Practical Implementation Guide

2026-08-31 · 5 min read · SubToAPI Team

Claude function calling — Anthropic calls it "tool use" — lets you give Claude a list of functions it can request to run, along with structured JSON arguments, so it can fetch live data, query a database, or trigger an action instead of guessing an answer from training data. You define the functions as JSON schemas, send them with your request, and Claude decides when to call one, returning a structured request that your code executes and feeds back into the conversation.

This guide focuses on the practical side: how to structure the request, run the tool-calling loop, handle multiple or parallel calls, and avoid the mistakes that break most first implementations.

The basic request shape

A function calling request has three parts: the messages array, a tools array describing what Claude can call, and (optionally) a tool_choice parameter to control when tools get used.

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_stock_price",
      "description": "Get the current price of a stock by ticker symbol",
      "input_schema": {
        "type": "object",
        "properties": {
          "ticker": {
            "type": "string",
            "description": "Stock ticker symbol, e.g. AAPL"
          }
        },
        "required": ["ticker"]
      }
    }
  ],
  "messages": [
    { "role": "user", "content": "What's Apple's stock price right now?" }
  ]
}

If Claude decides it needs the function, the response's content array includes a block with type: "tool_use", a name, and structured input. If it doesn't need a tool, you just get normal text.

Writing schemas Claude actually uses well

Function calling quality depends heavily on how you describe the tool, not just the JSON shape:

The tool-calling loop

Function calling is not a single request-response — it's a loop. Claude requests a tool, you run it, and you send the result back as a tool_result block so Claude can continue:

let messages = [{ role: "user", content: "What's Apple's stock price?" }];

let response = await callClaude(messages, tools);

while (response.stop_reason === "tool_use") {
  const toolUse = response.content.find(b => b.type === "tool_use");
  const result = await runLocalFunction(toolUse.name, toolUse.input);

  messages.push({ role: "assistant", content: response.content });
  messages.push({
    role: "user",
    content: [
      {
        type: "tool_result",
        tool_use_id: toolUse.id,
        content: JSON.stringify(result)
      }
    ]
  });

  response = await callClaude(messages, tools);
}

console.log(response.content);

The loop ends when stop_reason is no longer tool_use. For multi-tool workflows — say, looking up a customer then checking their order status — this loop can run several iterations before Claude produces a final answer.

Parallel tool calls

Claude can return multiple tool_use blocks in a single response when the tasks are independent — for example, checking the weather in three different cities at once. Your code should iterate over all tool_use blocks in content, run them (ideally concurrently), and send back a tool_result for each one, matched by tool_use_id, before continuing the conversation. Missing one result will stall the loop.

Controlling when tools get used

The tool_choice parameter gives you control over Claude's behavior:

Forcing a specific tool is useful when you're using function calling purely for structured output extraction rather than an actual side-effecting action — for example, forcing a save_extracted_data tool to get reliably formatted JSON out of unstructured text.

Handling errors gracefully

Two failure modes come up constantly:

  1. The function call itself fails (API down, invalid ticker, timeout). Don't drop the turn — send back a tool_result with an error message and "is_error": true so Claude can explain the failure to the user or retry with different input, instead of the conversation just hanging.
  2. Claude sends unexpected arguments. Validate input against your schema before running the function. Malformed input is rare with well-written schemas but not impossible, especially with free-form string fields.
{
  "type": "tool_result",
  "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
  "content": "Error: ticker symbol not found",
  "is_error": true
}

Function calling with streaming

Tool use works with streaming too, but the tool_use block arrives incrementally — input_json_delta events build up the arguments piece by piece, and you need to buffer them until the block closes before parsing the JSON. If you're building this from scratch, budget real testing time for this part; partial JSON parsing is the most common source of bugs in streaming tool implementations.

Running function calling in production

Once you move past prototyping, you're maintaining API key rotation, per-team usage tracking, and streaming infrastructure on top of the tool-calling logic itself. SubToAPI wraps Claude access behind a standard HTTPS API with sub_live_... application keys, so your backend calls one endpoint with full support for tool use, streaming, and usage metadata, without managing raw provider credentials per environment. Team and Scale plans add multi-seat dashboards if more than one service or developer needs isolated keys. See the tool use docs and streaming docs for request formats, or start with the quickstart.

Questions

Does Claude support parallel function calls in one response? Yes. Claude can return multiple tool_use blocks in a single response for independent tasks. Your code must send back a matching tool_result for each one before continuing.

How is Claude's function calling different from OpenAI's? The underlying idea is the same — JSON schemas describing callable functions, structured output requesting a call. Claude uses tool_use/tool_result content blocks inside the messages array rather than a separate function_call field, and tool_choice options differ slightly (auto, any, tool, none).

Can I force Claude to always call a specific function? Yes, using tool_choice: {"type": "tool", "name": "your_function"}. This is commonly used to get reliable structured JSON output from unstructured input, treating the tool schema as an extraction template rather than an action.

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 →