Skip to content

Exponential Backoff

A retry schedule that multiplies the wait after each failure (1 s, 2 s, 4 s, 8 s…) up to a cap, giving a rate-limited or struggling service time to recover.

Also called: Exponential retry · Binary exponential backoff · Backoff schedule

Reviewed

Interactive lab

Hands on: use the controls to explore. Anything that plays on its own pauses while you are working inside it.

Watch the waits double until the cap

7 / 7

Attempt 7 of 9

t = 1 min 1 s

SucceededDelivered after 6 retries
  1. Attempt 1 at 0 ms: failed.
  2. Attempt 2 at 1 s, after waiting 1 s: failed.
  3. Attempt 3 at 3 s, after waiting 2 s: failed.
  4. Attempt 4 at 7 s, after waiting 4 s: failed.
  5. Attempt 5 at 15 s, after waiting 8 s: failed.
  6. Attempt 6 at 31 s, after waiting 16 s: failed.
  7. Attempt 7 at 1 min 1 s, after waiting 30 s: succeeded.

The delay formula · retry n = 5

delayn = min(cap, base × factorn)

= min(30 s, 1 s × 25) = 30 scap reached (32 s uncapped)

sleep = delay = 30 s

Fires at t = 1 min 1 s, 30 s after attempt 6.

24 clients, same outage · retry n = 5

Without jitter all 24 retry at exactly 30 s: a thundering herd that can knock the server over again.

A send failing with 130429 retries on a 1 s base, factor 2 and a 30 s cap; the sixth retry is capped and succeeds. Toggle jitter to see the same schedule randomised.

Attempt 7 at 1 min 1 s succeeded.

01What is exponential backoff?

Exponential backoff is a retry schedule in which the wait before each new attempt grows geometrically. After the first failure a client waits a base delay; after the next it waits that delay times a multiplier, then times the multiplier again, and so on, until a maximum delay (the cap) is reached. With a 1-second base, a factor of 2 and a 30-second cap, retries wait 1, 2, 4, 8, 16, 30 and 30 seconds. The formula is delay = min(cap, base x factor^n), where n counts retries from zero.

The growing gaps matter because the most common reasons for a failed call (a server under load, a rate limit, a brief network fault) get worse when everyone retries immediately. Fixed or instant retries keep the pressure on; exponential gaps back off quickly, giving the dependency room to recover while still retrying fast for a one-off blip. Most clients pair it with a retry limit or an overall deadline, after which the job goes to a dead-letter queue or the error is reported.

On the WhatsApp Cloud API the classic triggers are Error 130429 (Rate Limit Hit), raised when a number exceeds its throughput (up to 80 messages per second by default), error 131056, the pair rate limit on messages to one recipient, HTTP 5xx responses and timeouts. Meta uses the same idea in the other direction: under its webhook retry policy, a failing webhook endpoint gets deliveries retried with decreasing frequency for up to seven days.

Exponential backoff is only the schedule. Backoff jitter randomises each delay so thousands of clients that failed together do not retry together (a thundering herd), and the retry policy decides which failures are worth retrying at all. Proactive rate limiting paces sends so the backoff rarely fires, and a circuit breaker stops retrying altogether for a while when a dependency is clearly down.

Key takeaways

  • Exponential backoff waits base x factor^n before retry n, so with a 1 s base and a factor of 2 the delays grow 1 s, 2 s, 4 s, 8 s.
  • A cap stops the waits growing forever; a retry limit or deadline decides when to give up and dead-letter the job.
  • Use it for transient failures only: timeouts, HTTP 5xx, throughput error 130429 and pair rate limit 131056.
  • Backoff sets the schedule; jitter randomises it and the retry policy decides whether to retry at all.
  • Meta applies its own decreasing-frequency retries to your webhook endpoint for up to 7 days.

02 · How it works, step by step

How does exponential backoff work?

  1. Step 1

    Classify the failure first

    Only transient errors enter the backoff loop: timeouts, connection resets, HTTP 5xx, 130429 and 131056. Permanent ones, such as an invalid recipient, a paused template or a missing permission, fail fast instead.

  2. Step 2

    Compute the next delay

    For retry n (starting at 0), wait base x factor^n. A factor of 2 doubles each wait; a smaller factor such as 1.5 grows more gently for latency-sensitive work like OTPs.

  3. Step 3

    Cap the delay

    Apply min(cap, delay) so the tenth retry does not wait seventeen minutes. Caps of 30 to 60 seconds suit interactive sends; background jobs can use longer.

  4. Step 4

    Randomise with jitter

    Replace the exact delay with a random value up to it (full jitter) so clients that failed at the same moment spread their retries out instead of returning in lockstep.

  5. Step 5

    Stop on a limit or deadline

    End after a fixed number of retries or when the message would no longer be useful, such as an OTP older than its expiry. Then dead-letter the job, record the error code and fbtrace_id, and alert if the rate climbs.

03Exponential backoff in code

const RETRYABLE_HTTP = new Set([429, 500, 502, 503, 504]);
const RETRYABLE_META = new Set([130429, 131056]); // throughput, pair rate limit
export function backoffDelay(retry, { baseMs = 1000, factor = 2, capMs = 30_000 } = {}) {
return Math.min(capMs, baseMs * factor ** retry); // 1s, 2s, 4s, 8s, 16s, 30s, 30s...
}
export async function sendWithBackoff(payload, { maxRetries = 6 } = {}) {
for (let retry = 0; ; retry++) {
let res;
let err = {};
try {
res = await fetch(`https://graph.facebook.com/v26.0/${process.env.PHONE_NUMBER_ID}/messages`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(10_000),
});
if (res.ok) return await res.json();
err = (await res.json().catch(() => ({}))).error ?? {};
} catch (e) {
err = { message: e.message }; // timeout or network error: transient (but may have been delivered)
}
const transient = !res || RETRYABLE_HTTP.has(res.status) || RETRYABLE_META.has(err.code);
if (!transient || retry >= maxRetries) {
throw Object.assign(new Error(err.message ?? "send failed"), { code: err.code, fbtrace_id: err.fbtrace_id });
}
const delay = backoffDelay(retry);
await new Promise((r) => setTimeout(r, Math.random() * delay)); // full jitter on top
}
}

Retry only transient failures, grow the wait geometrically, cap it, randomise it, and give up after a fixed number of retries.

04Exponential backoff: key facts and limits

Formula
delay = min(cap, base x factor^n), n = 0 for the first retry
Example schedule
1, 2, 4, 8, 16, 30 s: 61 s of waiting over 6 retries
Throughput error
130429 (default up to 80 mps per number)
Pair rate limit error
131056 (too fast to one recipient)
Never back off and retry
Invalid recipient, paused template, missing permission
Meta's webhook retries
Decreasing frequency for up to 7 days

05Example: A festive broadcast meets the throughput cap

A Surat saree wholesaler queues 40,000 marketing templates at 10 am. Twelve workers send as fast as they can, the number exceeds its throughput, and a share of requests return 130429. Before the fix, the workers retried instantly and the error rate stayed above 30% for twenty minutes. With backoff at a 1-second base, a factor of 2, a 30-second cap and full jitter, plus a shared pacing limit just under the number's throughput, the errors fall to a trickle within a minute and the campaign completes with no messages dead-lettered.

06 · Common mistakes, and the fix

Common mistakes with exponential backoff

  • No cap and no limit

    Without a cap, retry 12 waits over an hour; without a limit, a permanently broken job retries forever and hides the failure. Set both, and send exhausted jobs to a dead-letter queue with the last error attached.

  • Backing off on permanent errors

    Retrying an invalid phone number or a paused template just delays the inevitable and wastes throughput. Check the error code and fail fast on anything a retry cannot fix.

  • Retrying a send that may already have worked

    A timeout does not mean the message was not delivered. Before retrying a timed-out send that matters, check whether a wamid or status webhook arrived for it, or accept the small duplicate risk deliberately.

  • Backing off in lockstep

    Thousands of jobs that failed in the same second and use the same schedule all retry in the same second too. Add jitter, and pace sends with a shared limiter so the cap is rarely hit at all.

  • Ignoring the message's useful life

    An OTP retried after two minutes of backoff may arrive after the code expired. Bound retries by a deadline for time-sensitive messages, not just by an attempt count.

07 · Tips from the bench

Best practices for exponential backoff

  • Put the backoff in one shared send function, so every caller gets the same behaviour.
  • Log each retry with attempt number, delay, error code and fbtrace_id; the pattern tells you whether it is a cap or an outage.
  • Use a smaller base and factor for OTPs and a larger cap for bulk campaigns.
  • If a dependency returns a Retry-After header, treat it as the minimum wait.

08Why does exponential backoff matter?

Retries without backoff turn a small incident into a big one: a broadcast that hits the throughput cap and retries instantly multiplies its own traffic, collects more 130429 errors, and can drag other sends on the same number down with it. A well-chosen schedule recovers by itself from blips, sheds load during real trouble and keeps your workers from spinning. It is also the cheapest reliability fix there is: a dozen lines of code in the one function every send goes through.

09Exponential Backoff FAQs

What is a good base delay and cap for exponential backoff?+
A 0.5 to 1-second base, a factor of 2 and a 30 to 60-second cap suit most API sends. Shorten them for OTPs that expire quickly, and lengthen the cap for background jobs that can wait.
What is the difference between exponential backoff and jitter?+
Backoff decides how long the wait grows; jitter randomises each wait. Exponential backoff alone still lets clients that failed together retry together, so production code almost always adds jitter on top of the schedule.
Should I retry WhatsApp error 130429?+
Yes, with backoff, and also slow down. Error 130429 means the number exceeded its throughput, so retries succeed once you send more slowly; pace sends below the limit with a shared rate limiter so the error becomes rare.
How many retries should I allow?+
Enough to outlast a typical blip, usually 4 to 8. Set it from how long the message stays useful: a marketing send can wait minutes, while an OTP should give up within its expiry time.

From definition to your first message

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.