Claude API Tool Use: Parallel Calls Example
When Claude decides it needs more than one tool to answer a request, it can return multiple tool_use blocks in a single response instead of making you round-trip one tool call at a time. This is what people mean by "parallel tool calls" — Claude doesn't execute them concurrently itself, but it asks for several tool invocations at once, and your code is responsible for running them (ideally in parallel) and sending all the results back together.
This matters because sequential tool calling is slow: if a user asks "what's the weather in Paris and Tokyo, and convert 100 USD to EUR," a naive implementation makes three separate requests to Claude, each waiting on the last. With parallel tool use, Claude emits three tool_use blocks in one turn, you execute all three tools concurrently in your backend, and you send one message back with all three tool_result blocks. Below is a complete, working example.
How parallel tool calls actually work
The flow has four steps:
- You send a message with
toolsdefined and the user's request. - Claude responds with
stop_reason: "tool_use"and acontentarray that may contain more than onetool_useblock. - You execute every tool call (in parallel, using
Promise.allor similar), matching each result to itstool_use_id. - You send a new message back with a
userrole containing onetool_resultblock per tool call, in any order — Claude matches them by ID, not position.
Claude decides on its own whether a request needs parallel calls. You can't force it, but you increase the odds by defining independent, narrowly-scoped tools and phrasing prompts that clearly require multiple pieces of information.
Full example: weather + currency lookups
Tool definitions:
[
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
},
{
"name": "convert_currency",
"description": "Convert an amount from one currency to another",
"input_schema": {
"type": "object",
"properties": {
"amount": { "type": "number" },
"from": { "type": "string" },
"to": { "type": "string" }
},
"required": ["amount", "from", "to"]
}
}
]
Initial request:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"tools": [...],
"messages": [
{"role": "user", "content": "What is the weather in Paris and Tokyo, and convert 100 USD to EUR?"}
]
}'
Claude's response will have stop_reason: "tool_use" and a content array like this:
{
"content": [
{ "type": "text", "text": "I'll check both cities' weather and do the conversion." },
{ "type": "tool_use", "id": "toolu_01A", "name": "get_weather", "input": { "city": "Paris" } },
{ "type": "tool_use", "id": "toolu_01B", "name": "get_weather", "input": { "city": "Tokyo" } },
{ "type": "tool_use", "id": "toolu_01C", "name": "convert_currency", "input": { "amount": 100, "from": "USD", "to": "EUR" } }
],
"stop_reason": "tool_use"
}
Executing the calls in parallel
const toolUses = response.content.filter(block => block.type === "tool_use");
const results = await Promise.all(
toolUses.map(async (block) => {
const output = await runTool(block.name, block.input); // your dispatcher
return {
type: "tool_result",
tool_use_id: block.id,
content: JSON.stringify(output)
};
})
);
Sending results back
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01A", "content": "{\"temp_c\": 14, \"condition\": \"cloudy\"}" },
{ "type": "tool_result", "tool_use_id": "toolu_01B", "content": "{\"temp_c\": 22, \"condition\": \"sunny\"}" },
{ "type": "tool_result", "tool_use_id": "toolu_01C", "content": "{\"converted\": 92.1, \"currency\": \"EUR\"}" }
]
}
Append this as a new message after Claude's tool_use message and send the full conversation history again. Claude will then produce a final text response combining all three results.
Common mistakes
- Ordering results wrong: match by
tool_use_id, never by array position — Claude doesn't require you to preserve order. - Missing a result: if Claude emitted three
tool_useblocks, you must return threetool_resultblocks in the same follow-up message, or the next call will likely fail or hallucinate missing data. - Running tools sequentially anyway: the entire point of parallel tool use is to cut latency. If your dispatcher awaits each tool one by one, you've thrown away the benefit — use
Promise.allor your language's equivalent. - Forgetting error handling: if one tool call fails, return a
tool_resultwithis_error: trueand a short message instead of skipping it. Claude handles partial failures gracefully if you tell it what happened.
Where this fits with SubToAPI
If you're exposing Claude through your own product's backend — say, a support bot that calls internal tools, or an agent that hits multiple APIs per user query — you need the same tool_use and tool_result contract regardless of how you provision access. SubToAPI turns your existing Claude access into an HTTPS API with application keys (sub_live_...), so you can call /v1/messages with full tool-use support, including parallel tool calls, without managing separate billing per team member. See the tools documentation and the messages API reference for the exact request/response shape, or check streaming if you want to see tool_use blocks arrive incrementally. Plans start at the pricing page, and you can try it from signup.
Questions
Can I force Claude to always make parallel tool calls? No. Claude decides based on the prompt and tool definitions whether multiple independent calls are needed. You can nudge it by writing prompts that clearly require several distinct pieces of information and keeping tools narrowly scoped.
Do parallel tool calls run concurrently on Anthropic's side? No — Claude returns multiple tool_use requests in one response, but the actual execution happens in your code. Concurrency depends entirely on how you dispatch the calls (e.g., Promise.all).
What happens if I only return some of the tool_result blocks? Claude may stall, ask for the missing result, or produce an incomplete answer. Always return exactly one tool_result per tool_use_id from the previous turn, including error results for failed calls.