← Blog

Claude API Function Calling with Multiple Tools

2026-09-28 · 5 min read · SubToAPI Team

When you register more than one tool with the Claude API, Claude decides on its own which tool (or tools) to call based on the conversation and the schemas you provide. Function calling with multiple tools isn't a special mode you switch on — it's the same tools array and tool_use content block mechanism as single-tool calling, just with more entries in the array and more careful handling on your end.

The practical challenge isn't registering the tools — it's writing schemas Claude can reliably tell apart, handling the case where Claude wants to call several tools in one turn, and routing each tool_use block to the correct handler before sending results back. This article covers all three.

How multiple tools work in a single request

You pass an array of tool definitions in the tools parameter of your Messages API call. Each tool has a name, a description, and an input_schema (JSON Schema). Claude reads the user's message, looks at every tool description, and decides whether to respond with plain text, call one tool, or call several tools in the same response.

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {
          "city": { "type": "string" }
        },
        "required": ["city"]
      }
    },
    {
      "name": "get_flight_status",
      "description": "Get the status of a flight by flight number",
      "input_schema": {
        "type": "object",
        "properties": {
          "flight_number": { "type": "string" }
        },
        "required": ["flight_number"]
      }
    }
  ],
  "messages": [
    { "role": "user", "content": "Is it raining in Berlin, and is flight LH123 on time?" }
  ]
}

For this prompt, Claude will typically return two tool_use blocks in a single assistant message — one for get_weather, one for get_flight_status. This is the core thing to design around: with multiple tools available, your response handling must expect an array of tool calls, not just one.

Handling a response with several tool_use blocks

The assistant message's content is an array. When multiple tools are invoked, you'll see multiple tool_use objects mixed in with any text blocks. You need to loop over all of them, execute the corresponding function, and return a tool_result for each one — matched by tool_use_id — in a single follow-up user message.

const response = await client.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  tools,
  messages,
});

const toolResults = [];

for (const block of response.content) {
  if (block.type === "tool_use") {
    const result = await runTool(block.name, block.input);
    toolResults.push({
      type: "tool_result",
      tool_use_id: block.id,
      content: JSON.stringify(result),
    });
  }
}

messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });

A common bug is sending only one tool_result back when Claude asked for two. If any tool_use block is left without a matching result, the next request will fail or Claude will get confused about what happened. Always iterate over the full content array and answer every tool_use block, even if one of your functions errors out — in that case, return a tool_result with an error message rather than nothing.

Writing schemas that don't collide

With one tool, ambiguity rarely matters. With five or ten tools, vague names and descriptions cause Claude to pick the wrong one or call two tools when one would do. A few rules that hold up in practice:

If you're new to schema design specifically, our function calling with JSON Schema guide covers the structure in more depth; this article focuses on the multi-tool orchestration layer on top of it.

Forcing or restricting tool choice

Sometimes you want Claude to always use a specific tool, or to choose freely among a subset. The tool_choice parameter controls this:

This matters more as your tool count grows: forcing any on a request where you know a tool call is required (e.g., a structured extraction step) avoids Claude replying with plain text when you specifically need a tool_use block back.

Where a proxy layer helps

None of this logic changes if you're calling Claude directly or through a gateway, but a few things get easier with a thin API layer in front of your integration. SubToAPI turns your existing Claude access into a standard HTTPS API — sub_live_... keys, the same Messages-style request shape, and usage metadata per key — so you can run this multi-tool loop from a backend, share access across a team without sharing raw credentials, and see which key is generating the tool-call traffic.

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": [ ... ],
    "messages": [ { "role": "user", "content": "..." } ]
  }'

If you're building this for a team rather than a single script, the multi-tool loop above stays identical — only the key management and per-seat visibility change. See the quickstart and messages docs for the full request/response shape, and pricing if you want seats for a team rather than a single key.

questions

Can Claude call more than two tools in one response? Yes. There's no hard limit on how many tool_use blocks can appear in a single assistant message beyond the model's own judgment about the task — three or four in one turn is common for multi-step requests.

Does adding more tools slow down responses or hurt accuracy? Adding tools increases prompt size slightly (schemas count as input tokens) and can reduce selection accuracy if descriptions overlap. Keep tool count focused on what's relevant per use case and write distinguishing descriptions to keep accuracy high.

What happens if I don't return a tool_result for every tool_use block? The next API call will either error out or produce an assistant message that ignores the missing result, since Claude expects a result for each tool it invoked. Always match every tool_use_id with a corresponding tool_result before continuing the conversation.

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 →