Skip to content

Main classes and functions

aresilient

aresilient - Resilient HTTP request library with automatic retry logic.

This package provides resilient HTTP request functionality with automatic retry logic and backoff strategies. Built on top of the modern httpx library, it simplifies handling transient failures in HTTP communications, making your applications more robust and fault-tolerant.

Key Features
  • Automatic retry logic for transient HTTP errors (429, 500, 502, 503, 504)
  • Multiple backoff strategies: Exponential, Linear, Fibonacci, Constant, and custom
  • Optional jitter to prevent thundering herd problems
  • Retry-After header support (both integer seconds and HTTP-date formats)
  • Complete HTTP method support (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
  • Full async support for high-performance applications
  • Configurable timeout, retry attempts, backoff factors, and jitter
  • Enhanced error handling with detailed exception information
  • Callback/Event system for observability (logging, metrics, alerting)
  • Context manager API for managing request sessions
Example
>>> from aresilient import get
>>> from aresilient.backoff import LinearBackoff
>>> from aresilient.core.config import ClientConfig
>>> # Use default exponential backoff
>>> response = get("https://api.example.com/data")  # doctest: +SKIP
>>> # Use linear backoff strategy
>>> response = get(
...     "https://api.example.com/data",
...     config=ClientConfig(backoff_strategy=LinearBackoff(base_delay=1.0)),
... )  # doctest: +SKIP
>>> # Use context manager for multiple requests
>>> from aresilient import ResilientClient
>>> with ResilientClient(config=ClientConfig(max_retries=5)) as client:  # doctest: +SKIP
...     response1 = client.get("https://api.example.com/data1")
...     response2 = client.post("https://api.example.com/data2", json={"key": "value"})
...

aresilient.AsyncResilientClient

Asynchronous context manager for resilient HTTP requests.

This class provides an async context manager interface for making multiple HTTP requests with shared retry configuration. The client automatically manages the lifecycle of the underlying httpx.AsyncClient and applies consistent retry logic across all requests.

Two usage patterns are supported:

Scenario 1 - Two context managers (external lifecycle management): The httpx.AsyncClient is created and managed by an outer async with block, and passed into AsyncResilientClient. AsyncResilientClient does not close the underlying client when it exits, leaving full control to the caller. Use this pattern when you need to share a single httpx.AsyncClient across multiple AsyncResilientClient instances, or when you need to configure the client with headers, auth, proxies, etc. and want explicit lifecycle control.

.. code-block:: python

import httpx
from aresilient import AsyncResilientClient
from aresilient.core.config import ClientConfig

async with httpx.AsyncClient(headers={"Authorization": "Bearer token"}) as http_client:
    async with AsyncResilientClient(
        client=http_client, config=ClientConfig(max_retries=5)
    ) as client:
        response = await client.get("https://api.example.com/data1")
# http_client is closed here by the outer ``async with`` block

Scenario 2 - Single context manager (AsyncResilientClient manages lifecycle): An httpx.AsyncClient instance is passed inline (or omitted, in which case a default client is created). AsyncResilientClient enters and closes the underlying client automatically when the async with block exits. Use this pattern for the simplest usage when you don't need to reuse the httpx.AsyncClient outside the async with block.

.. code-block:: python

import httpx
from aresilient import AsyncResilientClient
from aresilient.core.config import ClientConfig

async with AsyncResilientClient(
    client=httpx.AsyncClient(), config=ClientConfig(max_retries=5)
) as client:
    response = await client.get("https://api.example.com/data1")
# httpx.AsyncClient is closed here by AsyncResilientClient

# Equivalent shorthand (AsyncResilientClient creates a default client):
async with AsyncResilientClient(config=ClientConfig(max_retries=5)) as client:
    response = await client.get("https://api.example.com/data1")

Parameters:

Name Type Description Default
config ClientConfig | None

Optional ClientConfig instance for retry configuration. If None, a default ClientConfig is used.

None
client AsyncClient | None

Optional httpx.AsyncClient instance to use for requests. If None, a new client is created with the default timeout.

None
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> from aresilient.core.config import ClientConfig
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient(config=ClientConfig(max_retries=5)) as client:
...         response1 = await client.get("https://api.example.com/data1")
...         response2 = await client.post(
...             "https://api.example.com/data2", json={"key": "value"}
...         )
...
>>> asyncio.run(main())  # doctest: +SKIP
# Client automatically closed after context exits
Note

All HTTP method calls (get, post, put, delete, patch, head, options, request) use the resilience parameters defined in the config passed to the constructor.

aresilient.AsyncResilientClient.__aenter__ async

__aenter__() -> Self

Enter the async context manager.

If the underlying httpx.AsyncClient is not yet open, it is entered and its lifecycle is managed by this context manager (closed on exit). If the client is already open (e.g. managed by an outer async with block), AsyncResilientClient uses it without closing it on exit.

Returns:

Type Description
Self

The AsyncResilientClient instance for making requests.

aresilient.AsyncResilientClient.__aexit__ async

__aexit__(
    exc_type: type[BaseException] | None,
    exc_val: BaseException | None,
    exc_tb: TracebackType | None,
) -> None

Exit the async context manager and close the underlying httpx client if this context manager opened it.

Parameters:

Name Type Description Default
exc_type type[BaseException] | None

Exception type if an exception occurred.

required
exc_val BaseException | None

Exception value if an exception occurred.

required
exc_tb TracebackType | None

Exception traceback if an exception occurred.

required

aresilient.AsyncResilientClient.delete async

delete(url: str, **kwargs: Any) -> Response

Send an HTTP DELETE request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the DELETE request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.delete("https://api.example.com/data")
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.get async

get(url: str, **kwargs: Any) -> Response

Send an HTTP GET request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the GET request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.get("https://api.example.com/data")
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.head async

head(url: str, **kwargs: Any) -> Response

Send an HTTP HEAD request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the HEAD request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.head("https://api.example.com/data")
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.options async

options(url: str, **kwargs: Any) -> Response

Send an HTTP OPTIONS request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the OPTIONS request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.options("https://api.example.com/data")
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.patch async

patch(url: str, **kwargs: Any) -> Response

Send an HTTP PATCH request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the PATCH request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.patch(
...             "https://api.example.com/data", json={"key": "value"}
...         )
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.post async

post(url: str, **kwargs: Any) -> Response

Send an HTTP POST request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the POST request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.post(
...             "https://api.example.com/data", json={"key": "value"}
...         )
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.put async

put(url: str, **kwargs: Any) -> Response

Send an HTTP PUT request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the PUT request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.put(
...             "https://api.example.com/data", json={"key": "value"}
...         )
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.AsyncResilientClient.request async

request(method: str, url: str, **kwargs: Any) -> Response

Send an HTTP request with automatic retry logic.

Parameters:

Name Type Description Default
method str

HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.).

required
url str

The URL to send the request to.

required
**kwargs Any

Additional keyword arguments passed to httpx.AsyncClient.request().

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Raises:

Type Description
HttpRequestError

If the request fails after all retries.

Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main():  # doctest: +SKIP
...     async with AsyncResilientClient() as client:
...         response = await client.request("GET", "https://api.example.com/data")
...
>>> asyncio.run(main())  # doctest: +SKIP

aresilient.HttpRequestError

Bases: RuntimeError

Exception raised when an HTTP request fails.

This exception captures comprehensive details about failed HTTP requests, including the request method, URL, status code, and the full response object when available. It supports exception chaining to preserve the original cause of the error.

Parameters:

Name Type Description Default
method str

The HTTP method used for the request (e.g., 'GET', 'POST').

required
url str

The target URL that was requested.

required
message str

A descriptive error message explaining the failure.

required
status_code int | None

The HTTP status code returned by the server, if the request reached the server. Defaults to None if the request failed before receiving a response.

None
response Response | None

The complete httpx.Response object containing headers, body, and other response details. Defaults to None if no response was received.

None
cause BaseException | None

The original exception that caused this error, used for exception chaining. Defaults to None.

None

Attributes:

Name Type Description
method

The HTTP method used for the request.

url

The target URL that was requested.

status_code

The HTTP status code, if available.

response

The full response object, if available.

Examples:

Raising an error for a failed GET request:

>>> from aresilient import HttpRequestError
>>> raise HttpRequestError(
...     method="GET",
...     url="https://api.example.com/data",
...     message="Request failed with status 404",
...     status_code=404,
... )  # doctest: +SKIP

aresilient.ResilientClient

Synchronous context manager for resilient HTTP requests.

This class provides a context manager interface for making multiple HTTP requests with shared retry configuration. The client automatically manages the lifecycle of the underlying httpx.Client and applies consistent retry logic across all requests.

Two usage patterns are supported:

Scenario 1 - Two context managers (external lifecycle management): The httpx.Client is created and managed by an outer with block, and passed into ResilientClient. ResilientClient does not close the underlying client when it exits, leaving full control to the caller. Use this pattern when you need to share a single httpx.Client across multiple ResilientClient instances, or when you need to configure the httpx.Client with headers, auth, proxies, etc. and want explicit lifecycle control.

.. code-block:: python

import httpx
from aresilient import ResilientClient
from aresilient.core.config import ClientConfig

with httpx.Client(headers={"Authorization": "Bearer token"}) as http_client:
    with ResilientClient(
        client=http_client, config=ClientConfig(max_retries=5)
    ) as client:
        response = client.get("https://api.example.com/data1")
# http_client is closed here by the outer ``with`` block

Scenario 2 - Single context manager (ResilientClient manages lifecycle): An httpx.Client instance is passed inline (or omitted, in which case a default client is created). ResilientClient enters and closes the underlying client automatically when the with block exits. Use this pattern for the simplest usage when you don't need to reuse the httpx.Client outside the with block.

.. code-block:: python

import httpx
from aresilient import ResilientClient
from aresilient.core.config import ClientConfig

with ResilientClient(
    client=httpx.Client(), config=ClientConfig(max_retries=5)
) as client:
    response = client.get("https://api.example.com/data1")
# httpx.Client is closed here by ResilientClient

# Equivalent shorthand (ResilientClient creates a default client):
with ResilientClient(config=ClientConfig(max_retries=5)) as client:
    response = client.get("https://api.example.com/data1")

Parameters:

Name Type Description Default
config ClientConfig | None

Optional ClientConfig instance for retry configuration. If None, a default ClientConfig is used.

None
client Client | None

Optional httpx.Client instance to use for requests. If None, a new client is created with the default timeout.

None
Example
>>> from aresilient import ResilientClient
>>> from aresilient.core.config import ClientConfig
>>> with ResilientClient(config=ClientConfig(max_retries=5)) as client:  # doctest: +SKIP
...     response1 = client.get("https://api.example.com/data1")
...     response2 = client.post("https://api.example.com/data2", json={"key": "value"})
...
# Client automatically closed after context exits
Note

All HTTP method calls (get, post, put, delete, patch, head, options, request) use the resilience parameters defined in the config passed to the constructor.

aresilient.ResilientClient.__enter__

__enter__() -> Self

Enter the context manager.

If the underlying httpx.Client is not yet open, it is entered and its lifecycle is managed by this context manager (closed on exit). If the client is already open (e.g. managed by an outer with block), ResilientClient uses it without closing it on exit.

Returns:

Type Description
Self

The ResilientClient instance for making requests.

aresilient.ResilientClient.__exit__

__exit__(
    exc_type: type[BaseException] | None,
    exc_val: BaseException | None,
    exc_tb: TracebackType | None,
) -> None

Exit the context manager and close the underlying httpx client if this context manager opened it.

Parameters:

Name Type Description Default
exc_type type[BaseException] | None

Exception type if an exception occurred.

required
exc_val BaseException | None

Exception value if an exception occurred.

required
exc_tb TracebackType | None

Exception traceback if an exception occurred.

required

aresilient.ResilientClient.delete

delete(url: str, **kwargs: Any) -> Response

Send an HTTP DELETE request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the DELETE request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.delete("https://api.example.com/data")
...

aresilient.ResilientClient.get

get(url: str, **kwargs: Any) -> Response

Send an HTTP GET request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the GET request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.get("https://api.example.com/data")
...

aresilient.ResilientClient.head

head(url: str, **kwargs: Any) -> Response

Send an HTTP HEAD request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the HEAD request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.head("https://api.example.com/data")
...

aresilient.ResilientClient.options

options(url: str, **kwargs: Any) -> Response

Send an HTTP OPTIONS request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the OPTIONS request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.options("https://api.example.com/data")
...

aresilient.ResilientClient.patch

patch(url: str, **kwargs: Any) -> Response

Send an HTTP PATCH request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the PATCH request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.patch("https://api.example.com/data", json={"key": "value"})
...

aresilient.ResilientClient.post

post(url: str, **kwargs: Any) -> Response

Send an HTTP POST request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the POST request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.post("https://api.example.com/data", json={"key": "value"})
...

aresilient.ResilientClient.put

put(url: str, **kwargs: Any) -> Response

Send an HTTP PUT request with automatic retry logic.

Parameters:

Name Type Description Default
url str

The URL to send the PUT request to.

required
**kwargs Any

Additional keyword arguments (see request() method).

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.put("https://api.example.com/data", json={"key": "value"})
...

aresilient.ResilientClient.request

request(method: str, url: str, **kwargs: Any) -> Response

Send an HTTP request with automatic retry logic.

Parameters:

Name Type Description Default
method str

HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.).

required
url str

The URL to send the request to.

required
**kwargs Any

Additional keyword arguments passed to httpx.Client.request().

{}

Returns:

Type Description
Response

An httpx.Response object containing the server's HTTP response.

Raises:

Type Description
HttpRequestError

If the request fails after all retries.

Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client:  # doctest: +SKIP
...     response = client.request("GET", "https://api.example.com/data")
...