Python for AIPython 3.10+ · Pydantic 2.12
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
34 small wins to finish your pathNext lesson →

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:

python
results = await asyncio.gather(coro1, coro2, coro3)
results = await asyncio.gather(*list_of_coroutines)

Starting three calls together

Examplefrom the async and await lesson
import asyncio
import time

async def ask_model_slowly(text):
    await asyncio.sleep(0.5)
    return f"answer to: {text}"
Example
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())

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.

Example
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())

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 awaitasyncio.gather
Three 0.5 s calls takeAbout 1.5 secondsAbout 0.5 seconds
Order of resultsThe loop's orderThe order you passed them
Too many at onceNever happensNeeds 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.
Watch out. gather without a limit sends every call at once. With hundreds of items, add a semaphore before the provider starts refusing requests.
Try it yourself
  • Change the semaphore to 5, then to 1, and compare the times.
  • Give gather ten 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.