Claude API Tool Use with Multiple Functions
Can Claude call multiple tools in one request?
Yes. When you pass several tool definitions in the tools array of a Claude API request, Claude can choose to call one, several, or none of them in a single response — including calling the same tool more than once with different arguments. This is sometimes called "parallel tool use," and it's the standard pattern for building agents that need to, say, check the weather and look up a calendar event in the same turn.
The confusing part for most developers isn't registering multiple functions — it's handling the response correctly when Claude returns more than one tool_use block, and knowing how to send back multiple tool_result blocks without breaking the conversation. This article covers both.
Defining multiple tools
Each tool in the tools array needs a unique name, a description that tells Claude when to use it, and an input_schema in JSON Schema format. There's no limit on how many tools you can register beyond general payload size — teams commonly ship 5 to 20 tools in a single request.
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
},
{
"name": "get_calendar_events",
"description": "Get events on a user's calendar for a given date",
"input_schema": {
"type": "object",
"properties": {
"date": { "type": "string", "format": "date" }
},
"required": ["date"]
}
}
],
"messages": [
{ "role": "user", "content": "What's the weather in Lisbon, and what's on my calendar for tomorrow?" }
]
}
Given a prompt like this, Claude typically returns two tool_use blocks in the same content array — one for get_weather, one for get_calendar_events — instead of asking a follow-up question or picking only one.
Handling multiple tool_use blocks in the response
The response content array can contain a mix of text and tool_use blocks. You need to iterate over all of them, not just the first one:
const response = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools: tools,
messages: messages,
});
const toolUseBlocks = response.content.filter(
(block) => block.type === "tool_use"
);
const toolResults = await Promise.all(
toolUseBlocks.map(async (block) => {
const result = await runTool(block.name, block.input);
return {
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(result),
};
})
);
A common mistake is assuming there's exactly one tool call per turn and only acting on response.content[0]. If Claude returns two or three tool calls and you only handle one, the conversation state becomes inconsistent and the next request will error out because Claude expects a tool_result for every tool_use_id it emitted.
Sending back multiple tool results
Each tool_result must reference the matching tool_use_id, and all results go into a single user message as a content array — you don't send separate messages per tool:
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: toolResults });
const followUp = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools: tools,
messages: messages,
});
If any tool fails, include the error as the content of its tool_result with is_error: true rather than omitting the block entirely. Claude needs a result for every tool it called, even if that result is an error message — skipping one will break the request.
Running tool calls in parallel vs sequentially
Because Claude can return multiple independent tool calls in one turn, it's worth running them concurrently rather than in a loop, especially if any of them involve network I/O (database lookups, external APIs, file reads). Promise.all in JavaScript or asyncio.gather in Python both work well here. Just make sure each result stays tagged with its original tool_use_id so you can rebuild the tool_result array in the right order — order doesn't strictly matter for Claude, but mismatched IDs do.
One edge case worth planning for: Claude might call the same tool twice with different arguments in one turn (e.g., get_weather for two different cities). Your tool runner needs to handle duplicate tool names gracefully rather than deduplicating by name.
Where SubToAPI fits in
If you're exposing Claude's tool-calling behavior behind your own product — for example, letting your app's users trigger actions through natural language — you still need API keys, usage tracking per customer, and streaming support on top of the raw Anthropic integration. SubToAPI turns your existing Claude access into an HTTPS API with application-level keys (sub_live_...), so you can issue a separate key per customer or environment, see usage per key, and keep tool-use requests flowing through the same /v1/messages endpoint your app already calls.
The tool-calling mechanics described above work the same way through SubToAPI — you send the same tools array and handle the same tool_use/tool_result structure. Check the tool use docs and the messages API reference for the exact request shape, or start with the quickstart if you're setting this up for the first time. Plans start at Solo (€9) with a free trial at signup; see pricing for team and scale tiers.
questions
Can Claude call more than two tools in a single turn? Yes, there's no fixed cap on how many tool calls Claude can return in one response — it depends entirely on how many distinct actions the prompt implies. Your code just needs to iterate over all tool_use blocks rather than assuming a fixed count.
What happens if I don't return a result for every tool call? The next API request will fail validation because Claude expects exactly one tool_result per tool_use_id from the prior turn. Always return a result or an error block for every tool call, even for tools that failed or were skipped.
Should I run multiple tool calls in parallel or one at a time? Parallel execution is usually better when tools are independent and involve I/O, since it reduces latency before you can send the follow-up request. Just keep track of each result's tool_use_id so you can match them back up correctly.