⏸️ Parallel workflow: many tasks at once
When tasks don't depend on each other, there's no need to wait in line. A parallel workflow starts them all together, waits for them to finish, and then combines what they found.
Running Claude calls in parallel is a general design pattern that you build in your own code, for example with Python's asyncio. Anthropic calls it parallelization in Building effective agents. One related built-in behaviour is a Claude API feature: Claude can request several tool calls in one response (parallel tool use). Your code can run them at the same time, then send all the results back together in one message.
What is it?
Parallel execution means running several independent tasks at the same time instead of one after another. A coordinating program (1) splits the work, (2) starts every task, (3) waits for all of them, and (4) combines the results into one answer.
- Independent task
- A task that doesn't need any other task's result to start.
- Concurrency
- Having several tasks in progress at once. While one waits for Claude to reply, the others keep going.
- Fan-out / fan-in
- Fan-out: send work to many tasks. Fan-in: collect their results back together.
- Aggregator
- The step that combines all the results into the final output.
- Timeout
- The longest you're willing to wait for a task before giving up on it.
- Rate limit
- The maximum number of requests an API accepts from you in a period of time.
Sequential and parallel compared
| Sequential | Parallel | |
|---|---|---|
| Order | One after another | All at once |
| Depends on the previous step? | Yes | No |
| Total time | Sum of all steps | About as long as the slowest task |
| Cost | One call per step | Same number of calls, but they happen in a burst |
| Example | Research, then write, then review | Research three competitors |
Why is it important?
- Speed: three 20-second tasks take about 20 seconds in parallel instead of 60.
- Focus: each task looks at one subject, which often gives better results than one huge request.
- Confidence: running the same check several times and comparing the answers (called "voting") can make results more reliable.
Real-world analogy
A manager needs to know about three competitors by the afternoon. Instead of one researcher studying them one by one, the manager gives each competitor to a different researcher. They work at the same time. When everyone is done, the manager puts their notes side by side and writes one summary. If one researcher is out sick, the summary still goes ahead, with a note about what's missing.
How does it work?
- Check independence: make sure no task needs another task's result.
- Distribute: create one task per item (one per competitor, document or question).
- Run together, with a limit on how many run at once and a timeout for each.
- Collect all the results, including which tasks failed.
- Combine them into the final answer and point out any gaps or disagreements.
There are two common versions. Sectioning gives each task a different piece of work. Voting gives every task the same piece of work and compares the answers.
Workflow diagram
Try it: sequential and parallel side by side
Sequential
Parallel
Practical example: researching three competitors
A small e-scooter start-up wants a quick comparison of three competitors from a file of collected notes.
- The program starts three Claude calls together, one for each of Acme Scooters, Bolt Rides and CityGlide.
- Each call returns 5 bullet points on pricing, strengths and weaknesses.
- If one call fails or takes too long, it's recorded as missing and the others still count.
- A final call combines the summaries into a comparison table and three opportunities, and points out where the summaries disagree.
Code example
Runnable Python using the official SDK's AsyncAnthropic client and Python's built-in asyncio. It needs pip install anthropic, the ANTHROPIC_API_KEY environment variable on your computer or server, and a competitor_notes.txt file.
# parallel_research.py: research three competitors at the same time, then combine.
import asyncio
import pathlib
import anthropic
client = anthropic.AsyncAnthropic() # async client: many calls can wait at the same time
MODEL = "claude-opus-5-5"
COMPETITORS = ["Acme Scooters", "Bolt Rides", "CityGlide"]
limit = asyncio.Semaphore(3) # at most 3 calls at once (protects cost and rate limits)
def text_of(response) -> str:
return "".join(b.text for b in response.content if b.type == "text")
async def research(company: str, notes: str) -> str:
"""One independent task: summarise one competitor."""
async with limit:
response = await client.messages.create(
model=MODEL,
max_tokens=16000,
messages=[{"role": "user", "content":
f"Using only the notes below, summarise {company}'s pricing, strengths and "
f"weaknesses in 5 bullet points.\n\n<notes>\n{notes}\n</notes>"}],
)
return text_of(response)
async def safe_research(company: str, notes: str, seconds: float = 120):
"""Add a timeout, so one slow or failed task can't sink the others."""
try:
return company, await asyncio.wait_for(research(company, notes), timeout=seconds)
except Exception as err: # a timeout or an API error
print(f"[warning] {company} failed: {type(err).__name__}")
return company, None
async def main():
notes = pathlib.Path("competitor_notes.txt").read_text()
# Fan-out: start every task now; gather waits until all are finished.
results = await asyncio.gather(*(safe_research(c, notes) for c in COMPETITORS))
# Fan-in: keep successes, remember failures.
found = {company: summary for company, summary in results if summary}
missing = [company for company, summary in results if not summary]
if not found:
raise RuntimeError("Every research task failed. Nothing to combine.")
combined = "\n\n".join(f"## {c}\n{s}" for c, s in found.items())
final = await client.messages.create(
model=MODEL,
max_tokens=16000,
messages=[{"role": "user", "content":
"Compare these competitor summaries in a short table, then list 3 opportunities for us. "
"If two summaries contradict each other, say so instead of picking one.\n\n" + combined}],
)
print(text_of(final))
if missing:
print("\nNote: no data for " + ", ".join(missing))
if __name__ == "__main__":
asyncio.run(main())
Expected output (wording varies):
| Company | Price | Strength | Weakness |
|---------------|------------|-------------------|-------------------|
| Acme Scooters | $0.30/min | Large fleet | Poor app ratings |
| Bolt Rides | $0.25/min | Cheapest | Few cities |
| CityGlide | Subscription | Loyal users | Slow expansion |
Opportunities: 1) A better app experience ...
Step-by-step explanation
anthropic.AsyncAnthropic()is the SDK's async client. Withawait, the program can wait for several replies at the same time instead of one by one.asyncio.Semaphore(3)is a "max 3 at once" counter. With 50 competitors, it would stop you sending 50 requests at once and hitting rate limits or a surprise bill.research()is one independent task. It doesn't know about the other competitors.safe_research()adds a timeout withasyncio.wait_forand catches errors, returningNoneinstead of crashing everything.asyncio.gather(...)is the fan-out: it starts every task and waits until all are done.- The
foundandmissinglines are the fan-in: sort the results into successes and failures. - The final call is the aggregator. It is told to point out contradictions rather than hide them.
- The program prints which competitors are missing, so the reader knows the report is incomplete.
Collecting and combining results
- Keep results labelled (company → summary) so nothing gets mixed up.
gatherreturns results in the same order as the tasks. - Use the same format for every task, such as the same bullet structure or the same JSON fields, so results are easy to compare.
- Combine with code when you can, for example adding up numbers or merging lists. Use Claude to combine only when judgement or writing is needed.
Managing timeouts, failures, costs and conflicts
⏱ Timeouts
Give every task a time limit. Decide in advance whether partial results are acceptable, as in this example, or whether everything must succeed.
💥 Failures
Catch errors per task. The SDK already retries temporary errors, so record what still fails and report it.
💰 Costs
Parallel runs don't reduce the number of calls. Ten tasks still cost ten calls. Limit how many run at once and how many you start.
⚖️ Conflicting results
Tell the aggregator to flag disagreements. For yes/no checks, use voting: run the check 3 times and go with the majority.
Use cases: when it helps, and when tasks must stay sequential
👍 Beneficial
- Summarising many separate documents
- Researching several companies, products or topics
- Checking content against several rules at once (tone, facts, safety)
- Voting: asking the same question several times to compare answers
👎 Must stay sequential
- Step B needs step A's output (draft → review)
- Tasks change the same file or record
- Order matters, such as "pay the invoice, then send the receipt"
- You are near your rate limit or budget
Advantages and limitations
✅ Advantages
- Much faster for independent work
- Focused tasks give clearer results
- One failure doesn't have to stop everything
⚠️ Limitations
- Same cost, spent in a short burst
- Can hit rate limits
- Combining results takes extra work
- Harder to debug than a straight line
🚫 Common mistakes
- Running dependent tasks in parallel
- No timeouts, so one stuck task blocks everything
- Unlimited concurrency
- Silently dropping failed tasks
Best practices
- Double-check that tasks are truly independent before running them in parallel.
- Set a concurrency limit and a timeout for every task.
- Use the same output format for every task.
- Always report which tasks failed or are missing.
- Ask the aggregator to show disagreements instead of hiding them.
- When Claude requests several tool calls in one response, run them together and send all the results back in a single message.
Knowledge check
1. When is a parallel workflow the right choice?
Parallel only works for independent tasks. Dependent steps belong in a sequential workflow.
2. Three tasks take 10, 20 and 30 seconds. About how long does running them in parallel take?
Parallel time is roughly the slowest task. Sequential time is the sum of all tasks (60 s).
3. Does running tasks in parallel reduce API cost?
Parallel saves time, not money. Use concurrency limits to control spending.
4. One of three research tasks times out. What is a good response?
Partial results with an honest note about gaps are usually better than nothing, and much better than made-up data.
5. What is "voting" in parallel workflows?
Voting increases confidence, for example by flagging content only if 2 out of 3 checks agree.
Mini project
Parallel document summariser.
- Put three short
.txtfiles in a folder (news articles work well). - Adapt
parallel_research.pyso each file is summarised in 3 bullet points by its own task. - Print how long the whole run took (use
time.perf_counter()), then try running the tasks one by one and compare. - Add a fourth, deliberately missing file name and check that your program reports it instead of crashing.
- Finally, combine the three summaries into a "what these articles have in common" paragraph.
Key takeaways
- Parallel workflows run independent tasks at the same time, then combine them.
- They save time, not money, so limit concurrency.
- Every task needs a timeout and its own error handling. Report anything that's missing.
- Ask the aggregator to show conflicts, and use voting when you need extra confidence.
- If a task needs another's result, keep it sequential.
Further learning
- Building effective agents, the "Parallelization" section (Anthropic engineering blog)
- How to implement tool use, including parallel tool use (official docs)
- Rate limits (official docs)
- Anthropic Python SDK, including the async client (official GitHub repository)