⏸️ 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.

What kind of thing is this?

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

SequentialParallel
OrderOne after anotherAll at once
Depends on the previous step?YesNo
Total timeSum of all stepsAbout as long as the slowest task
CostOne call per stepSame number of calls, but they happen in a burst
ExampleResearch, then write, then reviewResearch three competitors

Why is it important?

Real-world analogy

A team of researchers

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?

  1. Check independence: make sure no task needs another task's result.
  2. Distribute: create one task per item (one per competitor, document or question).
  3. Run together, with a limit on how many run at once and a timeout for each.
  4. Collect all the results, including which tasks failed.
  5. 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

The three research tasks run at the same time. Result collection waits until all of them have finished, failed or timed out, then the combined answer is written.

Try it: sequential and parallel side by side

Interactive demoA simulation with sped-up times. No AI is called.

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.

    1. The program starts three Claude calls together, one for each of Acme Scooters, Bolt Rides and CityGlide.
    2. Each call returns 5 bullet points on pricing, strengths and weaknesses.
    3. If one call fails or takes too long, it's recorded as missing and the others still count.
    4. 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

    1. anthropic.AsyncAnthropic() is the SDK's async client. With await, the program can wait for several replies at the same time instead of one by one.
    2. 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.
    3. research() is one independent task. It doesn't know about the other competitors.
    4. safe_research() adds a timeout with asyncio.wait_for and catches errors, returning None instead of crashing everything.
    5. asyncio.gather(...) is the fan-out: it starts every task and waits until all are done.
    6. The found and missing lines are the fan-in: sort the results into successes and failures.
    7. The final call is the aggregator. It is told to point out contradictions rather than hide them.
    8. The program prints which competitors are missing, so the reader knows the report is incomplete.

    Collecting and combining results

    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

    Knowledge check

    1. When is a parallel workflow the right choice?

    2. Three tasks take 10, 20 and 30 seconds. About how long does running them in parallel take?

    3. Does running tasks in parallel reduce API cost?

    4. One of three research tasks times out. What is a good response?

    5. What is "voting" in parallel workflows?

    Mini project

    Parallel document summariser.

    1. Put three short .txt files in a folder (news articles work well).
    2. Adapt parallel_research.py so each file is summarised in 3 bullet points by its own task.
    3. Print how long the whole run took (use time.perf_counter()), then try running the tasks one by one and compare.
    4. Add a fourth, deliberately missing file name and check that your program reports it instead of crashing.
    5. Finally, combine the three summaries into a "what these articles have in common" paragraph.

    Key takeaways

    Further learning