Claude API Tool Use: Parallel Function Calls
Does Claude support parallel function calls?
Yes. When you give Claude multiple tools and the task requires more than one independent action, Claude can return several tool_use blocks in a single response instead of making you round-trip one call at a time. This is Claude's version of parallel function calling: the model decides in one turn that it needs, say, a weather lookup and a currency conversion, and emits both tool requests together so your application can execute them concurrently and send both results back in one tool_result batch.
This matters because it directly affects latency and cost. If your agent needs three independent pieces of data, sequential tool calls mean three round trips to the model (each with its own network latency and token overhead). Parallel tool calls collapse that into two round trips total: one where Claude asks for all three tools, and one where you return all three results and get the final answer.
How parallel tool use actually works
The mechanics are the same as single tool use — you're just handling a list instead of a single item.
- You send a request with a
toolsarray describing each function's name, description, and input schema. - Claude's response has
stop_reason: "tool_use"and acontentarray that can contain multipletool_useblocks, each with its ownid,name, andinput. - Your code iterates over every
tool_useblock, executes the corresponding function, and builds atool_resultblock for each one. - You send a single follow-up message containing all the
tool_resultblocks (matched bytool_use_id), and Claude continues the conversation using every result at once.
Here's what a response with parallel calls looks like:
{
"stop_reason": "tool_use",
"content": [
{ "type": "text", "text": "I'll check both cities for you." },
{
"type": "tool_use",
"id": "toolu_01A",
"name": "get_weather",
"input": { "city": "Lisbon" }
},
{
"type": "tool_use",
"id": "toolu_01B",
"name": "get_weather",
"input": { "city": "Porto" }
}
]
}
And the follow-up you send back:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A",
"content": "Lisbon: 22°C, clear"
},
{
"type": "tool_result",
"tool_use_id": "toolu_01B",
"content": "Porto: 18°C, light rain"
}
]
}
The key implementation detail is that every tool_use_id in the response needs a matching tool_result before you send the next message. If you only return results for some of the calls, the conversation will error or stall. A common bug is handling tool calls with a for loop that awaits sequentially but forgets one branch — always collect results into an array keyed by tool_use_id and send them all together.
When Claude chooses parallel over sequential
Claude decides whether to parallelize based on whether the tool calls are independent. If tool B's input depends on tool A's output, Claude will call them sequentially across turns. If the calls don't depend on each other — multiple lookups, multiple searches, multiple API checks for different entities — Claude is more likely to batch them into one turn.
You can nudge this behavior with your system prompt. If you want Claude to be aggressive about parallelizing independent work, say so explicitly:
When a user request requires multiple independent lookups,
call all the relevant tools in a single turn rather than
one at a time.
If you want strictly sequential behavior (useful when calls have side effects, like writing to a database), tell Claude to call one tool, wait for the result, then decide the next step.
Executing parallel calls safely in your backend
A few practical rules make parallel tool execution reliable:
- Run calls concurrently, not sequentially. Use
Promise.all(JavaScript) orasyncio.gather(Python) so independent tool calls actually execute in parallel instead of blocking on each other. - Isolate failures. If one tool call throws an error, don't let it crash the batch — catch it and return an error string as the
tool_resultcontent so Claude can react (e.g., retry, skip, or inform the user). - Preserve ordering by ID, not position. Match results to calls using
tool_use_id, never array index, since Claude's content array can interleave text and tool_use blocks. - Cap concurrency for rate-limited tools. If your tools hit external APIs with their own rate limits, use a concurrency limiter even though Claude issued the calls in parallel.
const toolUses = response.content.filter(b => b.type === "tool_use");
const results = await Promise.all(
toolUses.map(async (block) => {
try {
const output = await runTool(block.name, block.input);
return { type: "tool_result", tool_use_id: block.id, content: output };
} catch (err) {
return {
type: "tool_result",
tool_use_id: block.id,
content: `Error: ${err.message}`,
is_error: true
};
}
})
);
Keeping tool-use infrastructure simple
Parallel tool use adds real value, but it also adds real surface area: concurrency bugs, partial failures, and token overhead from larger tool schemas. If you're running this against the Claude API directly through your own Anthropic subscription, you're managing all of that infrastructure yourself — key rotation, per-request logging, and figuring out which tool calls failed across a team of developers testing the same agent.
SubToAPI turns your existing Claude access into a standard HTTPS API with per-application keys, streaming, and full usage metadata, so you can see exactly which tool calls happened and how many tokens each parallel batch consumed, without building that logging layer yourself. Setup takes a few minutes — grab a key at /signup, check the /docs/tools guide for request/response shapes, and compare plans on /pricing if you're rolling this out to a team.
Questions
Can Claude make an unlimited number of parallel tool calls in one turn? There's no fixed hard limit in the API, but in practice Claude batches a reasonable number of independent calls at once. Very large tool sets or deeply interdependent tasks tend to get split across turns regardless.
Do parallel tool calls cost more tokens than sequential ones? The input tokens are similar either way since you're sending the same tool definitions, but parallel calls save you the overhead of extra round trips, which reduces latency and total request count significantly.
What happens if I only return some tool_result blocks? The conversation will error or Claude will respond with confusion about missing data. Always match every tool_use_id from the response with exactly one tool_result before sending the next message.