asyncio.gather
asyncio.gather is a function that runs several coroutines at the same time and returns their results in the order you passed them.
Last updated: 30 Sep, 2026 · Python 3.14
async and await awaited each call before starting the next. gather starts them together, so the batch takes about as long as the slowest call.
Syntax:
results = await asyncio.gather(coro1, coro2, coro3)
results = await asyncio.gather(*list_of_coroutines)Starting three calls together
import asyncio
import time
async def ask_model_slowly(text):
await asyncio.sleep(0.5)
return f"answer to: {text}"async def main():
texts = ["ticket 1", "ticket 2", "ticket 3"]
start = time.perf_counter()
answers = await asyncio.gather(*[ask_model_slowly(text) for text in texts])
print(answers)
print(f"took {time.perf_counter() - start:.1f} seconds")
asyncio.run(main())['answer to: ticket 1', 'answer to: ticket 2', 'answer to: ticket 3'] took 0.5 seconds
The list comprehension makes three coroutines without awaiting any. gather runs them together and returns their results as a list, in the same order as the coroutines you gave it, whichever finished first.
The * in front of the list unpacks it: gather(*[a, b, c]) is the same call as gather(a, b, c). gather wants separate arguments, not one list.
Limiting calls with a semaphore
Model APIs limit how many requests you may send at a time. Starting a thousand calls together gets most of them refused. A semaphore lets only a set number through at once.
limit = asyncio.Semaphore(2)
async def ask_politely(text):
async with limit:
return await ask_model_slowly(text)
async def main():
start = time.perf_counter()
answers = await asyncio.gather(*[ask_politely(f"ticket {n}") for n in range(1, 6)])
print(len(answers), "answers")
print(f"took {time.perf_counter() - start:.1f} seconds")
asyncio.run(main())5 answers took 1.5 seconds
async with limit: waits for one of the two places to be free, runs the block, and gives the place back. Five calls, two at a time, half a second each: three rounds.
One by one vs gather
for with await | asyncio.gather | |
|---|---|---|
| Three 0.5 s calls take | About 1.5 seconds | About 0.5 seconds |
| Order of results | The loop's order | The order you passed them |
| Too many at once | Never happens | Needs a semaphore |
Where gather shows up in AI code
- Sorting a batch of tickets, or embedding a batch of documents, with one call each.
- Asking several models the same question and comparing the answers.
gather without a limit sends every call at once. With hundreds of items, add a semaphore before the provider starts refusing requests.Related
- Previous: async and await
- Next: Timeouts and retries
- Reference: asyncio.gather in the Python docs
- Change the semaphore to 5, then to 1, and compare the times.
- Give
gatherten tickets without the semaphore. - Remove the
*and read the error. It talks about a dict key: gather took the whole list as one argument it cannot use, which is why it wants the items as separate arguments.
Slow is fine. Stopping is the only problem.