mrkeyoor.com_
Wed 23 Sept 02:13 UTC
PyPIUtilsupdated 21 Sept 2026

aiohttp-retry review

aiohttp-retry 2.9.1 wraps aiohttp's ClientSession with repeat attempts, status and exception filters, and five wait strategies. The current patch fixes a case where the client sometimes stopped without making the retry it owed; 2.9.0 added filtering by HTTP method. It can reuse an existing session, vary the URL or headers between attempts, and expose the current attempt through aiohttp tracing. Our Python 3.12 install imported successfully in 0.59 seconds and included py.typed, so typed async applications can use it without a separate stub package.

Verdict

aiohttp-retry 2.9.1 installed in 0.5 seconds and occupied 10 MB in our sandbox, with a working typed import and 0 audit findings. Install it for shared aiohttp retry policy, but use a general retry library when failures outside HTTP need the same treatment.

We installed it

Lab card: what happened when we installed aiohttp-retryScreenshot of aiohttp-retry documentation
Install✓ · 0.5s11 packages on disk · 10 MB
Importimport aiohttp_retry in 0.59s · pure Python · py.typed · requires Python >=3.7
Known vulns0(pip-audit)

Answers from our run

Does aiohttp-retry install cleanly?

Yes. In a fresh container with an empty cache, pip install aiohttp-retry finished in 0.5s, leaving 11 packages and 10 MB on disk. pip-audit reported no known vulnerabilities.

What does aiohttp-retry need to run?

Python >=3.7, and nothing compiled: it is pure Python. In our run import aiohttp_retry succeeded in 0.59s, and the package ships py.typed for type checkers.

aiohttp-retry or tenacity: which should you use?

tenacity: Use it when decorators and retry policies must cover both HTTP calls and other Python operations. aiohttp-retry 2.9.1 installed in 0.5 seconds and occupied 10 MB in our sandbox, with a working typed import and 0 audit findings.

When should you not use aiohttp-retry?

The same retry rules must cover database calls, queue operations, and ordinary functions. Tenacity applies retry policy outside aiohttp too.

API stability3/5Version 2.9.1 keeps the ClientSession-shaped methods and constructor-based retry options, and its release only fixes a missed-retry path. The history still matters: 2.5.6 added the response argument to custom get_timeout methods, while 2.7.0 through 2.8.2 were yanked after a response-callback bug caused endless retries. Pinning a current release is safer than accepting any 2.x build.
Docs3/5The README documents the five wait strategies, session reuse, method-level overrides, exact 5xx filtering, changing parameters between attempts, response callbacks, and current_attempt tracing. It also states what happens on the final response and exception. There is no separate API reference for this package, and the README directs readers to the test suite for more examples, so uncommon constructor details require source reading.
Maintenance3/5GitHub showed an unarchived repository with 273 stars, 13 open issues and pull requests, and a push on April 8, 2026. PyPI still serves 2.9.1, released November 6, 2024, whose changelog fixes a client path that sometimes failed to retry. Repository activity has continued after the last package release, so a merged change is not proof that the published wheel contains it.
Ecosystem3/5The stored registry snapshot counts 9,318,754 weekly downloads, and the wrapper accepts aiohttp sessions, request arguments, and TraceConfig callbacks directly. Our install found one direct dependency and py.typed metadata. Its useful ecosystem is intentionally narrow: policies cannot be attached to HTTPX, requests, database work, or arbitrary coroutines without adopting a second retry tool.

Use it if

  • Your application already uses aiohttp and needs one retry policy shared across several request sites.
  • Retries must react to named exceptions, HTTP statuses, request methods, or an async response-body check.
  • Later attempts need different URLs or headers, such as moving from a primary endpoint to a backup.
  • aiohttp TraceConfig hooks must receive the current attempt number for logs or metrics.
Skip it if

Setup reality

We installed aiohttp-retry 2.9.1 in a fresh unprivileged Python 3.12 container. It finished in 0.5 seconds and left 11 packages using 10 MB. The package has 1 direct dependency, is pure Python, requires Python 3.7 or newer, and ships py.typed. import aiohttp_retry completed in 0.59 seconds. pip-audit found 0 known vulnerabilities in that resolved environment.

No credentials or configuration file are built in. RetryClient accepts the same connection, timeout, cookie, proxy, TLS, and trace settings as aiohttp ClientSession. If you pass an existing ClientSession, your code owns its lifetime and must close it. If RetryClient creates the session, close the retry client or use async with RetryClient() so connectors are released.

The default policy retries every 5xx response as well as statuses you add. Set retry_all_server_errors=False when the status set must be exact. The attempts value counts the first request, so 3 means no more than 3 total calls. Per-request retry_options replace the client's options; they do not inherit the remaining client fields. The final response is returned even when it still matches the retry rule, while a final exception is raised.

Response callbacks need extra care. Versions 2.7.0 through 2.8.2 were yanked because the callback path could retry forever, and 2.8.3 fixed it. A custom get_timeout method must accept response; that argument can be None when no server response exists or raise_for_status interrupted the path. Retrying writes also needs a server-supported idempotency key because this package cannot undo a duplicated side effect.

Patterns

Retry a GET three times retry-get

from aiohttp_retry import RetryClient, ExponentialRetry

async def fetch(url: str) -> str:
    async with RetryClient(retry_options=ExponentialRetry(attempts=3)) as client:
        async with client.get(url) as response:
            response.raise_for_status()
            return await response.text()

attempts=3 permits 3 total requests, including the initial call.

Wrap an existing ClientSession reuse-session

from aiohttp import ClientSession
from aiohttp_retry import RetryClient

async with ClientSession() as session:
    client = RetryClient(client_session=session)
    async with client.get('https://example.com') as response:
        body = await response.text()

The outer context owns and closes the injected ClientSession.

Retry rate limits and gateway errors retry-statuses

from aiohttp_retry import ExponentialRetry

policy = ExponentialRetry(
    attempts=4,
    statuses={429, 502, 503, 504},
)

All 5xx responses are already retried by default; adding statuses mainly matters for 429 here.

Retry only one server status allowlist-server-errors

policy = ExponentialRetry(
    attempts=3,
    retry_all_server_errors=False,
    statuses={503},
)

Without retry_all_server_errors=False, other 5xx responses remain retryable.

Limit retries to safe methods filter-methods

policy = ExponentialRetry(
    attempts=3,
    methods={'GET', 'HEAD'},
)

HTTP method filtering arrived in 2.9.0. Add writes only when the application supplies idempotency protection.

Spread retries with jitter add-jitter

from aiohttp_retry import JitterRetry, RetryClient

client = RetryClient(retry_options=JitterRetry(attempts=4))

Jitter reduces synchronized repeat traffic when many workers see the same outage.

Set an exact delay sequence fixed-schedule

from aiohttp_retry import ListRetry

policy = ListRetry(timeouts=[0.5, 2.0, 5.0])

ListRetry derives its attempt count from the number of timeout entries.

Retry a malformed JSON response inspect-response

from aiohttp import ClientResponse
from aiohttp_retry import ExponentialRetry

async def unusable(response: ClientResponse) -> bool:
    payload = await response.json()
    return payload.get('state') != 'ready'

policy = ExponentialRetry(attempts=3, evaluate_response_callback=unusable)

Use 2.8.3 or newer because older callback releases in the 2.7.0 to 2.8.2 range were yanked for an infinite-retry bug.

Replace policy for one request override-one-call

from aiohttp_retry import RandomRetry

async with client.get(
    'https://example.com/flaky',
    retry_options=RandomRetry(attempts=6),
) as response:
    print(response.status)

The request policy replaces the client policy. Its values are not merged with the client defaults.

Switch endpoints between attempts fail-over-endpoint

from aiohttp_retry import RequestParams

async with client.requests(params_list=[
    RequestParams(method='GET', url='https://primary.example.com/data'),
    RequestParams(method='GET', url='https://backup.example.com/data'),
]) as response:
    data = await response.json()

When attempts outnumber entries, the final RequestParams entry is reused.

Log the current attempt trace-attempt

from aiohttp import TraceConfig

async def on_start(session, ctx, params):
    print(ctx.trace_request_ctx['current_attempt'])

trace = TraceConfig()
trace.on_request_start.append(on_start)

RetryClient adds current_attempt to aiohttp's trace_request_ctx for each dispatched call.

Cap a custom backoff custom-delay

from aiohttp_retry import RetryOptionsBase

class CappedRetry(RetryOptionsBase):
    def get_timeout(self, attempt: int, response=None) -> float:
        return min(2 ** attempt, 30)

The response parameter is required for current custom strategies and may be None.

Alternatives

PackageRegistryPick it when
tenacityPyPIUse it when decorators and retry policies must cover both HTTP calls and other Python operations.
staminaPyPIUse it for a smaller opinionated layer over Tenacity with jitter and test-mode controls.
aiohttpPyPIKeep aiohttp alone when one short local loop is clearer than a project-wide retry abstraction.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.