Retries
Retries in Zapros are implemented as a handler — wrapping another handler to automatically retry failed requests with exponential backoff.
Setup
from zapros import (
AsyncClient,
RetryHandler,
AsyncStdNetworkHandler,
)
client = AsyncClient(
handler=RetryHandler(AsyncStdNetworkHandler())
)from zapros import (
Client,
RetryHandler,
StdNetworkHandler,
)
client = Client(handler=RetryHandler(StdNetworkHandler()))Basic usage
By default, RetryHandler retries requests that fail with specific status codes or network errors:
from zapros import (
AsyncClient,
RetryHandler,
AsyncStdNetworkHandler,
)
client = AsyncClient(
handler=RetryHandler(AsyncStdNetworkHandler())
)
async with client:
response = await client.request(
"GET",
"https://api.example.com/data",
)from zapros import (
Client,
RetryHandler,
StdNetworkHandler,
)
client = Client(handler=RetryHandler(StdNetworkHandler()))
with client:
response = client.request(
"GET",
"https://api.example.com/data",
)Default behavior
Status codes
The following status codes are retried automatically:
429- Too Many Requests (rate limit)500- Internal Server Error502- Bad Gateway503- Service Unavailable504- Gateway Timeout
Safe HTTP methods
Only idempotent methods are retried by default:
GET,HEAD,PUT,DELETE,OPTIONS,TRACE
Non-idempotent methods like POST and PATCH are not retried on status codes to avoid duplicate operations.
Network errors
Pre-transmission errors are always retried, regardless of HTTP method:
ConnectionError,ConnectionRefusedError- Connection timeouts and DNS errors
- SSL/Certificate errors
client = AsyncClient(
handler=RetryHandler(AsyncStdNetworkHandler())
)
async with client:
response = await client.request(
"POST",
"https://api.example.com/create",
json={"name": "test"},
)Configuration
Customize retry behavior with these parameters:
from zapros import (
AsyncClient,
RetryHandler,
AsyncStdNetworkHandler,
)
client = AsyncClient(
handler=RetryHandler(
AsyncStdNetworkHandler(),
max_attempts=5,
backoff_factor=1.0,
backoff_max=120.0,
backoff_jitter=0.5,
)
)Parameters
max_attempts(default:4) - Maximum number of attempts (including the initial request)backoff_factor(default:0.5) - Base delay multiplier for exponential backoffbackoff_max(default:60.0) - Maximum delay between retries in secondsbackoff_jitter(default:1.0) - Jitter factor to randomize delays (0.0 to 1.0)
Backoff calculation
Delay between retries follows exponential backoff:
delay = min(backoff_factor * (2 ** attempt), backoff_max)With jitter applied to prevent thundering herd:
jitter_range = delay * backoff_jitter
delay = delay ± random(jitter_range)Custom retry policy
Implement your own retry logic using the RetryPolicy protocol:
from zapros import (
Request,
Response,
RetryHandler,
AsyncStdNetworkHandler,
AsyncClient,
)
class CustomRetryPolicy:
def should_retry(
self,
*,
request: Request,
response: Response | None,
error: Exception | None,
attempt: int,
) -> bool:
if error is not None:
return True
if response and response.status == 418:
return True
return False
client = AsyncClient(
handler=RetryHandler(
AsyncStdNetworkHandler(),
policy=CustomRetryPolicy(),
max_attempts=3,
)
)Retry on specific errors
from zapros import TimeoutError
class TimeoutRetryPolicy:
def should_retry(
self,
*,
request: Request,
response: Response | None,
error: Exception | None,
attempt: int,
) -> bool:
if error and isinstance(error, TimeoutError):
return True
return FalseRetry POST requests
class AlwaysRetryPolicy:
def should_retry(
self,
*,
request: Request,
response: Response | None,
error: Exception | None,
attempt: int,
) -> bool:
if error:
return True
if response and response.status >= 500:
return True
return False
client = AsyncClient(
handler=RetryHandler(
AsyncStdNetworkHandler(),
policy=AlwaysRetryPolicy(),
)
)
async with client:
response = await client.request(
"POST",
"https://api.example.com/create",
json={"data": "value"},
)Combining with other handlers
Chain RetryHandler with other handlers like cookies or auth:
from zapros import (
AsyncClient,
RetryHandler,
CookieHandler,
AsyncStdNetworkHandler,
)
client = AsyncClient(
handler=RetryHandler(
CookieHandler(AsyncStdNetworkHandler()),
max_attempts=3,
)
)