aiohttp ClientTimeout: the 300 s default and Sume polling

aiohttp's default total timeout is 5 minutes. Set ClientTimeout total and sock_read for Sume status polls, and keep a job deadline in your own loop.

5 min readSume
All posts

Short answer

By default aiohttp applies a total timeout of 300 seconds, or 5 minutes, to the whole operation, per the aiohttp client docs. That is far longer than a Sume status read should ever take, so set a smaller ClientTimeout with a total and a sock_read value on the session, and keep the job deadline in your own loop.

The four fields

ClientTimeout has four timeout fields that matter here (the docs also list ceil_threshold). total covers the whole operation, including connection, sending and reading. connect covers connection establishment or waiting for a free connection in the pool. sock_connect covers connecting to a peer for a new connection. sock_read is the longest gap allowed between data portions from the peer.

aiohttp ClientTimeout fields, per the docs (read 2026-10-03)
FieldBounds
totalThe whole operation: connect, send and read
connectNew connection, or waiting for a pooled one
sock_connectConnecting to a peer for a new connection
sock_readThe gap between reading data portions

Why the default is wrong for polling

Five minutes of tolerance per request means a stalled status read blocks one poll for five minutes, and your loop gets slower than the job. Sume status replies are small, so a total of 30 seconds is plenty. If you also download large result files, give the download its own session with sock_read set and a longer total, or none, since a healthy transfer should not be cut at a fixed length.

Sume's sync mode can wait up to 30 seconds on the server, so a total below 30 would abort a sync request that is behaving normally. Prefer async mode: submit, receive a 202 with a job id, then poll.

The session

Pass the timeout when you create the session, and set the key header once. The sample reads the key from an environment variable and sends x-api-key alone. Sending both Authorization and x-api-key is rejected with 401 unauthorized, so avoid mixing a default header with a per-request one.

import asyncio
import os

import aiohttp


async def main():
    timeout = aiohttp.ClientTimeout(total=30, sock_read=15)
    headers = {"x-api-key": os.environ["SUME_API_KEY"]}
    async with aiohttp.ClientSession(timeout=timeout, headers=headers) as s:
        async with s.get("https://api.sume.com/v1/me") as r:
            print(r.status, await r.json())


asyncio.run(main())

The deadline belongs to your loop

A per-request timeout does not say how long you will wait for a job. Wrap the loop in asyncio.timeout (Python 3.11 and later), or track a deadline value, and stop polling when it passes. When it does, the job keeps running and is still billed. Read it later, or cancel it while it is still cancelable. Do not resubmit as a new request.

Inside the loop, read terminal and next_poll_after_seconds from each reply and sleep that long. A 429 names its budget in error.details.scope and carries retry-after; wait that long before the next read.

Per-request overrides

The docs also let you pass a timeout to an individual request such as session.get, using the same ClientTimeout object. That is handy when one session serves both quick status reads and a rare slow call. Keep the session default tight and widen only the call that needs it, rather than widening the default for everything.

Finally, close the session. Create one ClientSession for the life of the poller and reuse it, so connections are pooled and the timeout settings apply uniformly.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume