Skip to main content

backoffkit

Retry decorator with exponential backoff and jitter. Works with sync and async functions. Zero dependencies.

PyPI version Python versions CI License: MIT Open Source


Any time your code calls something external — an API, a database, a third-party service — it can fail temporarily. The fix is to retry, but retrying naively (immediately, forever) makes things worse.

backoffkit gives you smart retry logic in one decorator:

from backoffkit import retry

@retry(times=3, backoff="exponential")
def call_api():
    response = requests.get("https://api.example.com/data")
    response.raise_for_status()
    return response.json()

It works the same way for async functions:

@retry(times=3, backoff="exponential")
async def fetch_data():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.example.com/data")
        response.raise_for_status()
        return response.json()

Install

pip install backoffkit

Usage

Basic retry

from backoffkit import retry

@retry(times=3)
def call_api():
    ...

Retry only on specific exceptions

@retry(times=3, on=[ConnectionError, TimeoutError])
def call_api():
    ...

Log retries with a callback

import logging

@retry(
    times=5,
    on=[ConnectionError],
    on_retry=lambda attempt, exc: logging.warning(f"Retry {attempt}: {exc}")
)
def call_api():
    ...

Async support

@retry(times=3, backoff="exponential")
async def fetch():
    ...

Backoff strategies

Strategy Description Delays (delay=1s)
exponential Doubles each attempt (default) 1s, 2s, 4s, 8s...
linear Increases by delay each attempt 1s, 2s, 3s, 4s...
fixed Same delay every attempt 1s, 1s, 1s, 1s...

Jitter

Jitter adds a small random noise to each delay to prevent the thundering herd problem — when many clients retry at exactly the same time and overwhelm the server. Enabled by default.

@retry(times=3, backoff="exponential", jitter=True)  # default
def call_api():
    ...

All options

@retry(
    times=3,           # Total attempts including the first (default: 3)
    delay=1.0,         # Initial delay in seconds (default: 1.0)
    backoff="exponential",  # "fixed", "linear", or "exponential" (default: "exponential")
    jitter=True,       # Add random noise to delay (default: True)
    max_delay=60.0,    # Cap on delay in seconds (default: 60.0)
    on=[Exception],    # Exceptions to retry on (default: all)
    on_retry=None,     # Callback: fn(attempt: int, exc: Exception) -> None
)

Error handling

When all attempts are exhausted, RetryError is raised with the attempt count and the last exception:

from backoffkit import retry, RetryError

@retry(times=3, delay=0)
def fn():
    raise ConnectionError("service down")

try:
    fn()
except RetryError as e:
    print(e.attempts)         # 3
    print(e.last_exception)   # ConnectionError: service down

Production use

backoffkit is designed for production backend systems. Use it when calling:

  • External REST APIs
  • Databases that may have transient connection failures
  • Message queues
  • Any I/O-bound operation that can fail temporarily

The exponential backoff + jitter combination is the industry-standard approach used by AWS, Google, and Stripe SDKs.


Why not tenacity?

tenacity is powerful but has a large API surface. backoffkit covers the 95% use case with a single decorator and zero dependencies — no learning curve, no extras to install.


Open Source

backoffkit is MIT licensed and open for contributions. See CONTRIBUTING.md.

Things we'd love help with:

  • Async callback support in on_retry
  • on_success and on_failure hooks
  • Retry budget (max total wait time across all retries)
  • More backoff strategies (fibonacci, decorrelated jitter)

License

MIT © Sufiyan Khan

Metadata

Release files for backoffkit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for backoffkit 0.1.0
File Size Uploaded
backoffkit-0.1.0.tar.gz 6.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for backoffkit 0.1.0
File Interpreter ABI Platform
backoffkit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 11.6 kB

Release files / backoffkit-0.1.0.tar.gz

Download URL backoffkit-0.1.0.tar.gz
Size 6.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0367dbec7cf4ee07429abc639147f8d3802fc00c50a1f371abfa31b77f6e9afc
BLAKE2b-256 checksum
How to use checksums
77026510159abecb6821b0b69e8f8f4f9796ccafae63ef9bb09618b009a67cd0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release files / backoffkit-0.1.0-py3-none-any.whl

Download URL backoffkit-0.1.0-py3-none-any.whl
Size 5.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8d412de5ad8084dc14aa7d9a5657af33e8cbf5114ced40dde4dae40e8937f806
BLAKE2b-256 checksum
How to use checksums
68c1beed9ef7b137a822e117430d0407f881ffed304243575a36eb87a3ee32fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.5

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page