Claude API Function Calling: Parallel Execution Guide
When you give Claude multiple tools and ask it a question that needs more than one of them, Claude can return several tool_use blocks in a single response instead of asking one at a time. This is what "parallel execution" means in the context of Claude API function calling: Claude decides which tools to call, emits all of them in one turn, and your application is responsible for running those calls concurrently and sending all the results back together. It is not a special API parameter you toggle on — it's a behavior pattern you need to design your client code around.
The practical problem developers run into is this: if you write your tool-handling loop assuming one tool_use block per response, you'll either miss calls, process them sequentially (killing latency), or send back malformed tool_result messages that break the conversation. This article covers how parallel tool calls actually show up in the response, how to execute them concurrently, and how to return results in the format Claude expects.
How Claude returns multiple tool calls
When Claude's response includes tool use, the content array can contain more than one tool_use block, each with its own id, name, and input. A typical response body looks like this:
{
"id": "msg_01...",
"role": "assistant",
"content": [
{ "type": "text", "text": "Let me check both." },
{ "type": "tool_use", "id": "toolu_01A", "name": "get_weather", "input": { "city": "Lisbon" } },
{ "type": "tool_use", "id": "toolu_01B", "name": "get_weather", "input": { "city": "Porto" } }
],
"stop_reason": "tool_use"
}
Claude is not telling you to run these one after another — it's handing you a batch. Your job is to execute every tool_use block in that array, then send a single user message containing one tool_result block per id, in any order, inside one content array.
Executing tool calls concurrently
The code pattern that handles this correctly loops over all tool_use blocks, dispatches them in parallel with Promise.all (or your language's equivalent), and collects results keyed by id:
const toolUses = response.content.filter(b => b.type === "tool_use");
const results = await Promise.all(
toolUses.map(async (block) => {
const output = await runTool(block.name, block.input);
return {
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(output)
};
})
);
// send results back in one message
messages.push({ role: "assistant", content: response.content });
messages.push({ role: "user", content: results });
A few things matter here:
- Match
tool_use_idexactly. If you reorder or drop an id, Claude cannot reconcile the result with the call and the conversation will error or hallucinate. - Return all results together. Don't send partial results as separate messages — one
tool_resultper matchingtool_use, bundled in a single user turn. - Handle failures per-tool, not globally. If one tool call throws, catch it and return an error string inside that specific
tool_resultrather than failing the whole batch. Claude can reason about a failed sub-task if you tell it what happened.
async function runTool(name, input) {
try {
return await tools[name](input);
} catch (err) {
return { error: err.message };
}
}
Why concurrency matters for latency
If your three tools each take 400ms and you run them sequentially, you've added 1.2 seconds to the turn before Claude even sees the results. Running them with Promise.all collapses that to roughly the slowest single call. For anything that hits external APIs, databases, or search indexes, this difference is the main lever you have over perceived response time in a tool-using assistant.
Forcing or discouraging parallel calls
You can influence whether Claude batches calls by how you design your tools and prompt. If a task genuinely requires sequential dependency (tool B needs tool A's output), Claude will usually call them one turn at a time naturally, since it can't invent A's result to pass into B. If you want to encourage batching for independent lookups, make that explicit in the system prompt: "When multiple independent lookups are needed, request them in the same turn." Claude follows this instruction reliably in practice.
Conversely, the tool_choice parameter can force a single named tool call ({"type": "tool", "name": "..."}), which disables parallelism for that turn by definition, or force any tool use at all ({"type": "any"}), which leaves batching behavior unconstrained.
Where SubToAPI fits
If you're already running Claude through SubToAPI, the tool-use request and response shape is the same as the native Messages API — you send your tool definitions, Claude returns tool_use blocks (single or multiple), and you run the same Promise.all pattern against your own functions before posting tool_result back. SubToAPI's value here is operational: a stable sub_live_... application key instead of personal credentials, usage metadata per request so you can see which tool-heavy conversations are costing the most, and the same endpoint whether you're testing locally or running in production across a team. See /docs/tools for the request format and /docs/messages for the full conversation loop, including how tool results feed back into subsequent turns. If you're also streaming responses while tools are in play, /docs/streaming covers how tool_use blocks appear in a streamed response.
Getting a working key takes a couple of minutes via /signup, and plans starting at Solo (€9) scale up to Team and Scale tiers with per-seat billing if more than one developer needs access — details on /pricing.
Practical checklist
- Loop over the full
contentarray, not just the firsttool_useblock. - Execute all tool calls concurrently, not in sequence.
- Return one
tool_resultpertool_use_id, bundled into a single message. - Catch per-tool errors and report them inline instead of failing the batch.
- Use system prompt guidance if you want to nudge batching behavior either way.
FAQ
Does Claude always call tools in parallel when it can? No. It calls tools in parallel when the task has independent sub-steps it recognizes as separable in one turn. Dependent steps (where one tool's output feeds another) are naturally sequential across turns.
Can I force Claude to only call one tool at a time? Yes, by setting tool_choice to a specific named tool, which limits that turn to a single call. Without a constraint, Claude may still batch independent calls in the next turn.
What happens if I don't return results for every tool_use block? The conversation will likely error or Claude will produce an unreliable next turn, since it's missing the output it expected for a call it made. Always return a matching tool_result for every id you receive.