Back to examples
Error Handling
Exponential Backoff
Retry with exponentially increasing delays between attempts.
When to use
Use this when calling rate-limited APIs or services that need progressively longer recovery windows.
error-handling/exponential-backoff.py
"""
02_exponential_backoff.py: Retry with exponentially increasing delays.
Demonstrates:
- retry_delay_seconds=[0.01, 0.05, 0.1] (list form)
- Each retry uses the next delay in the list; the last value is reused if retries exceed list length
"""
from dagy import flow, task
_calls: dict[str, int] = {}
@task(retries=3, retry_delay_seconds=[0.01, 0.05, 0.1])
def unstable_api(endpoint: str) -> dict:
"""Simulates an API that is unavailable for the first two calls."""
_calls[endpoint] = _calls.get(endpoint, 0) + 1
call = _calls[endpoint]
print(f" unstable_api({endpoint!r}) call #{call}")
if call < 3:
raise ConnectionError(f"Service unavailable (call #{call})")
return {"endpoint": endpoint, "data": "OK", "call": call}
@task
def handle_response(response: dict) -> str:
return f"Got response from {response['endpoint']} on call #{response['call']}"
@flow(name="exponential_backoff_flow")
def exponential_backoff_flow(endpoint: str = "/api/data") -> None:
response = unstable_api(endpoint)
handle_response(response)
if __name__ == "__main__":
result = exponential_backoff_flow.run_local(endpoint="/api/data")
print(f"Run ID : {result.run_id}")
print(f"Status : {result.status}")How it works
- `retry_delay_seconds=[0.01, 0.05, 0.1]` specifies increasing delays for each retry.
- If retries exceed the list length, the last value is reused.
- `unstable_api` simulates a service that is unavailable for the first two calls.
- On the third call it succeeds, and `handle_response` formats the result.
Inputs
- `endpoint` (str, default `"/api/data"`): API endpoint to call.
Outputs
- A formatted response string.
Dependencies
`dagy`