Exponential Backoff with Jitter for Cloud API Calls in Python

A Python snippet demonstrating exponential backoff with jitter for retrying transient cloud API failures, using a simulated client that has a configurable success rate.

Medium Python 3.9+ Aug 9, 2026 Cloud + Python 18 views 0 copies

Python code

38 lines
Python 3.9+
import random
import time


def exponential_backoff_with_jitter(retries=5, base_delay=0.5, max_delay=4.0, jitter_factor=0.3):
    for attempt in range(1, retries + 1):
        delay = min(max_delay, base_delay * (2 ** (attempt - 1)))
        jitter = delay * random.uniform(-jitter_factor, jitter_factor)
        effective_delay = max(0, delay + jitter)
        print(f"Attempt {attempt}: waiting {effective_delay:.2f}s before retry")
        time.sleep(effective_delay)
    print("All retries exhausted, giving up.")
    return False


class CloudClientMock:
    def __init__(self, success_rate=0.4, seed=42):
        random.seed(seed)
        self.success_rate = success_rate

    def request(self):
        if random.random() < self.success_rate:
            return "success"
        raise ConnectionError("simulated transient network failure")


if __name__ == "__main__":
    client = CloudClientMock(success_rate=0.4)
    for attempt in range(1, 5):
        try:
            result = client.request()
            print(f"Attempt {attempt}: {result}")
            break
        except ConnectionError as err:
            print(f"Attempt {attempt}: {err}")
            exponential_backoff_with_jitter(retries=3, base_delay=0.2, max_delay=1.5)
    else:
        print("Final failure: no successful request after retries.")

Output

stdout
Attempt 1: simulated transient network failure
Attempt 1: waiting 0.20s before retry
Attempt 2: waiting 0.37s before retry
Attempt 3: waiting 0.70s before retry
All retries exhausted, giving up.
Attempt 2: simulated transient network failure
Attempt 1: waiting 0.20s before retry
Attempt 2: waiting 0.37s before retry
Attempt 3: waiting 0.70s before retry
All retries exhausted, giving up.
Attempt 3: success

How it works

The exponential_backoff_with_jitter function calculates an exponential delay based on the attempt number, then adds a random jitter within a configured range to avoid synchronized retries. CloudClientMock simulates a flaky cloud endpoint by raising ConnectionError with a configurable probability. In the main block, each failed attempt triggers a full backoff sequence; a successful request breaks the loop. The random.seed ensures reproducible behavior, and the print statements show exactly when retries happen and when they are given up. This pattern is production-ready for API clients where transient failures are common and thundering-herd effects need to be avoided.

Common mistakes

  • Forgot to cap the delay with `max_delay`, leading to excessively long waits on high retry counts.
  • Using a fixed retry delay instead of adding jitter, which can cause thundering herd on service recovery.
  • Not seeding random when reproducibility matters for tests or debugging.
  • Calling `time.sleep` for the full effective delay without checking if the total timeout budget is exceeded.

Variations

  1. Replace `time.sleep` with `asyncio.sleep` and make the function async for non-blocking retries in event-loop-based code.
  2. Use a decorator to wrap any function with the backoff logic, so the retry policy is reusable across different clients.

Real-world use cases

  • Retrying transient failures in cloud SDK calls, like AWS S3 uploads, when network hiccups occur.
  • Adding jitter to database connection retry loops to prevent all service instances from hammering a recovering database simultaneously.
  • Building a resilient webhook delivery worker that backs off and retries failed HTTP posts to third-party services.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Cloud + Python

Related tutorials and quizzes for this topic.