What is exponential backoff? Formula, jitter and examples

Exponential backoff is a retry strategy in which the wait before each new attempt grows exponentially, usually doubling: 1 second, then 2, 4, 8, 16, up to a maximum delay. It gives a struggling service room to recover instead of hammering it while it's down. In practice it's almost always combined with jitter (randomness) and a cap on the delay and the number of attempts.

delay(n) = min(cap, base × 2ⁿ)
with full jitter: sleep(random(0, delay(n)))

base = 1s, cap = 60s
attempt:  1    2    3    4    5     6     7
delay:    1s   2s   4s   8s   16s   32s   60s

Why not just retry immediately?

Immediate or fixed-interval retries cause two problems:

  • They add load at the worst moment. A server returning 503 because it's overloaded gets more requests from every client retrying.
  • They synchronise clients. If 10,000 requests fail together and all retry after exactly 5 seconds, the server gets 10,000 requests in the same instant again. This is the "thundering herd".

Backoff fixes the first, and jitter fixes the second.

Example with full jitter

async function withBackoff(fn, { base = 1000, cap = 60_000, maxAttempts = 7 } = {}) {
  for (let attempt = 0; ; attempt++) {
    try {
      return await fn()
    } catch (err) {
      if (attempt + 1 >= maxAttempts || !isRetryable(err)) throw err
      const retryAfter = err.retryAfterMs            // from a Retry-After header, if any
      const backoff = Math.min(cap, base * 2 ** attempt)
      const delay = retryAfter ?? Math.random() * backoff
      await new Promise((r) => setTimeout(r, delay))
    }
  }
}

const isRetryable = (err) =>
  err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT' ||
  err.status === 429 || err.status >= 500

Jitter strategies

StrategyDelayNotes
No jitterbase × 2ⁿClients stay synchronised
Full jitterrandom(0, base × 2ⁿ)Spreads load best, a good default
Equal jitterd/2 + random(0, d/2)Guarantees a minimum wait
Decorrelated jitterrandom(base, prev × 3)Grows from the previous delay

AWS's architecture blog analysed these and found full and decorrelated jitter do the least total work.

What to retry, and what not to

  • Retry: timeouts, connection resets, DNS failures, 429, 500, 502, 503, 504.
  • Don't retry blindly: 400, 401, 403, 404, 422. They'll fail the same way until something changes.
  • Honour Retry-After. If the server says when to come back, wait at least that long.
  • Make retries safe. Retrying a POST that half-succeeded can create duplicates unless the operation is idempotent, for example by sending an Idempotency-Key header.

Exponential backoff in webhooks

Webhook providers use backoff for webhook retries: early retries come quickly to cover blips, later ones are hours apart to cover outages. Real schedules are often hand-tuned rather than pure doubling. The Webhook Relay Outbound Gateway, for instance, retries after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours, up to 8 attempts within 48 hours.

For inbound webhooks, Webhook Relay's durable retries run fast retries for a handoff window (15 minutes by default), then retry with exponential backoff on the schedule you choose: Seconds (about 25 minutes total), Medium (about 16 hours) or Long (up to 30 days). Details are in the durable webhooks docs.

If the problem is a burst rather than an outage, backoff alone isn't enough. Throttling releases queued webhooks to your endpoint at a steady rate or concurrency, so it never sees the spike.

Common mistakes

  • No cap. Without a maximum, the 20th retry waits 12 days.
  • No attempt limit or deadline. Eventually give up and move the work to a dead-letter queue for inspection.
  • Retrying at several layers. If the HTTP client, the SDK and your job queue all retry with backoff, one failure can become dozens of attempts.
  • Backing off in the request path. Sleeping inside a web request ties up a worker. Schedule the retry in a queue instead.

Frequently asked questions

What is exponential backoff?

Exponential backoff is a retry strategy in which the wait before each new attempt grows exponentially, usually doubling: 1 second, 2, 4, 8 and so on, up to a maximum delay. It gives a failing service time to recover and stops many clients from retrying in lockstep.

What is the formula for exponential backoff?

The delay before attempt n is base multiplied by 2 to the power of n, capped at a maximum: delay = min(cap, base * 2^n). With jitter, you pick a random value between zero and that delay instead of using it exactly.

What is jitter and why add it?

Jitter is randomness added to each backoff delay. Without it, every client that failed at the same moment retries at the same moment again, creating spikes that can keep the service down. Full jitter, a random delay between zero and the computed backoff, spreads retries out evenly.

When should I not use exponential backoff?

Don't retry errors that will never succeed, such as a 400 for an invalid request or a 401 for a bad API key, without changing something first. If the server sends a Retry-After header, wait at least that long instead of your computed delay.