Skip to content

Glossary drawing · Reliability & scale

Backoff Jitter

Also called: Jitter · Full jitter · Decorrelated jitter · Randomised backoff

Note 1 —Backoff jitter adds randomness to retry delays so clients that failed together do not retry together, spreading the load on a recovering API.

Term
Backoff Jitter
Abbreviation
—
Category
Reliability & scale
Level
Expert
Reviewed
Sheet no.
GLO-REL-675
Drawn by
InfiQ Glossary

Drawing 1 · Interactive

Toggle jitter and watch the retry herd scatter

5 / 5

Attempt 5 of 7

t = 7.87 s

SucceededDelivered after 4 retries
  1. Attempt 1 at 0 ms: failed.
  2. Attempt 2 at 461 ms, after waiting 461 ms: failed.
  3. Attempt 3 at 880 ms, after waiting 419 ms: failed.
  4. Attempt 4 at 3.11 s, after waiting 2.23 s: failed.
  5. Attempt 5 at 7.87 s, after waiting 4.75 s: succeeded.

The delay formula · retry n = 3

delayn = min(cap, base × factorn)

= min(30 s, 1 s × 23) = 8 s

sleep = random(0, 8 s) = 4.75 s

Fires at t = 7.87 s, 4.75 s after attempt 4.

24 clients, same outage · retry n = 3

Full jitter spreads their retries across the window: at most 5 land together, so the recovering server is not flattened.

One failed send retrying with a 1 s base, factor 2 and a 30 s cap. Switch between no jitter and full jitter, draw new random waits, and compare how 24 clients that failed together land on the server.

Attempt 5 at 7.87 s succeeded.

What is backoff jitter?

Backoff jitter is the randomness added to the delay between retries. Exponential Backoff decides how long the waits should grow — 1 s, 2 s, 4 s, 8 s, up to a cap — but on its own it keeps every client on the same schedule. If 5,000 sends from one broadcast fail in the same second because of a brief Cloud API error, plain backoff makes all 5,000 retry exactly 1 s later, then 2 s, then 4 s: synchronised spikes that look like a fresh traffic surge to the service trying to recover. Jitter breaks that lockstep.

Three variants are common. Full jitter picks a uniformly random wait between zero and the exponential delay: sleep = random(0, min(cap, base × 2^n)). Equal jitter keeps half of the delay and randomises the other half: t = min(cap, base × 2^n), then sleep = t/2 + random(0, t/2), which guarantees a minimum wait. Decorrelated jitter grows from the previous sleep rather than the attempt number: sleep = min(cap, random(base, previous × 3)). A widely cited simulation of many clients competing for one resource found no jitter worst by a wide margin, equal jitter doing slightly more work and taking clearly longer than the other two, and full jitter doing the least total work while decorrelated jitter finished slightly sooner.

Jitter is the randomisation layer; exponential backoff is the underlying schedule, and the Retry Policy decides whether a failure should be retried at all. Jitter matters most where many clients share one failure — a Broadcast hitting Error 130429 (Rate Limit Hit) because it exceeded the number's throughput, a fleet of workers reconnecting after a deploy, or thousands of webhook handlers calling a CRM that has just come back. That synchronised surge is the Thundering Herd, and jitter is its cheapest cure.

Key takeaways

  • Backoff jitter adds randomness to each retry delay so clients that failed at the same moment spread their retries out.
  • Full jitter waits random(0, min(cap, base × 2^attempt)); it spreads retries across the whole window and is a sound default.
  • Equal jitter keeps half the delay fixed and randomises the rest, which guarantees a minimum wait between attempts.
  • Decorrelated jitter grows from the previous wait: min(cap, random(base, previous × 3)).
  • Without jitter, thousands of sends that failed together retry together and can knock a recovering service over again.

Operating sequence

How does backoff jitter work?

  1. Compute the exponential ceiling

    For retry n, take min(cap, base × factor^n). With a 1 s base, factor 2 and a 30 s cap the ceilings run 1, 2, 4, 8, 16, 30, 30 s.

  2. Draw a random wait inside it

    Full jitter: a uniform random value between 0 and the ceiling. Equal jitter: half the ceiling plus a random value up to the other half. Decorrelated: a random value between the base and three times the previous sleep, capped.

  3. Respect the server's own hint

    If a response says when to come back — an HTTP Retry-After header, or a known rule such as waiting at least 24 hours after a per-user marketing-limit error (131049) — use it as the floor and add jitter on top.

  4. Cap attempts and total time

    Stop after a fixed number of attempts or a deadline that matches the message's value. An OTP is useless once its validity has passed, so it should never be retrying an hour later.

  5. Give up cleanly

    When retries run out, move the job to a dead-letter queue with its last error, rather than retrying forever or dropping it silently.

Specifications

Backoff jitter: key facts and limits

Full jitter
random(0, min(cap, base × 2^n))
Equal jitter
t/2 + random(0, t/2), where t = min(cap, base × 2^n)
Decorrelated jitter
min(cap, random(base, previous × 3))
Typical settings
Base 0.5–1 s, cap 30–60 s, 5–8 attempts (illustrative)
Retry with jitter
130429, 131056, 131000, 131016, HTTP 5xx, timeouts
Never retry
Invalid parameters (132000), re-engagement (131047)

Implementation

Backoff jitter in code

type Jitter = "none" | "full" | "equal" | "decorrelated";
const BASE_MS = 1_000;
const CAP_MS = 30_000;
/** Wait before retry number `attempt` (0-based). `prevMs` feeds decorrelated jitter. */
export function backoffDelay(attempt: number, mode: Jitter, prevMs = BASE_MS): number {
const ceiling = Math.min(CAP_MS, BASE_MS * 2 ** attempt);
switch (mode) {
case "none":
return ceiling;
case "full":
return Math.random() * ceiling;
case "equal":
return ceiling / 2 + Math.random() * (ceiling / 2);
case "decorrelated":
return Math.min(CAP_MS, BASE_MS + Math.random() * (prevMs * 3 - BASE_MS));
}
}
// Transient Graph errors worth retrying: throughput, pair rate, unknown, service, generic
const RETRYABLE = new Set([130429, 131056, 1, 2, 131000, 131016]);
export async function sendWithRetry(payload: object, maxAttempts = 6) {
const url = `https://graph.facebook.com/v26.0/${process.env.PHONE_NUMBER_ID}/messages`;
let prev = BASE_MS;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (res.ok) return res.json();
const code = (await res.json()).error?.code;
if (res.status < 500 && !RETRYABLE.has(code)) throw new Error(`Not retryable: ${code}`);
const hintMs = Number(res.headers.get("retry-after")) * 1000 || 0;
prev = Math.max(hintMs, backoffDelay(attempt, "full", prev));
await new Promise((resolve) => setTimeout(resolve, prev));
}
throw new Error("Retries exhausted: move the job to the dead-letter queue");
}

All three jitter variants, and a send loop that retries only transient errors with full jitter and honours Retry-After.

Example: A 50,000-message Diwali broadcast from Ahmedabad

A sweets chain in Ahmedabad queues 50,000 marketing templates across 40 workers. A misconfigured rate limiter lets them burst past the number's 80 messages per second, and about 6,000 sends fail with 130429 within a few seconds. With plain exponential backoff every one of them retries exactly 1 s later, triggering a second wave of 130429s, then a third at 2 s and a fourth at 4 s. After the team fixes the limiter and switches to full jitter (1 s base, 30 s cap), the same 6,000 retries spread across each window, the token bucket in front of the sender absorbs them, and the broadcast finishes with no second wave.

Design intent

Why does backoff jitter matter?

Retries are meant to ride out a blip, but synchronised retries can turn a two-second blip into a ten-minute outage. For a WhatsApp sender the cost is concrete: throttled numbers that keep slamming into their throughput ceiling, a CRM that falls over each time it recovers, and broadcasts that finish late. Jitter is a two-line change that spreads the same retry volume evenly across the window, so the dependency sees a smooth slope instead of a wall.

Failure modes

Common mistakes with backoff jitter

  • Jittering the wrong errors

    Jitter makes retries polite, not useful. A 132000 parameter mismatch or a 131047 re-engagement error fails on every attempt; send it to the dead-letter queue instead of retrying it, however randomly.

  • Seeding every worker identically

    Workers seeded with a fixed value for reproducible tests, or forked with a copied generator state (some libraries do not reseed after a fork), draw the same "random" sequence and retry in lockstep anyway. Seed per process, or use the runtime's default random source in production.

  • Jitter without a cap

    Uncapped exponential growth reaches waits of many minutes after a handful of failures, and full jitter can still draw near the top. Cap each delay and the total retry time per message.

  • Retrying inside and outside at once

    An HTTP client with its own retries, wrapped in a queue that also retries, multiplies attempts — 3 × 5 becomes 15 calls per message. Retry in one layer and make the others fail fast.

Recommended practice

Best practices for backoff jitter

  • Default to full jitter; reach for equal jitter only when a minimum wait matters more than spreading load.
  • Log the drawn delay with each retry so you can see the spread during an incident.
  • Pair jitter with a client-side token bucket algorithm so retries also respect the number's throughput.
  • Test with many concurrent clients failing at once — a single-client test cannot show a herd.

Backoff Jitter FAQs

Why add jitter to exponential backoff?+
Because backoff alone keeps clients that failed together on the same schedule, so they retry together. Jitter randomises each wait, turning synchronised retry spikes into a spread-out trickle the recovering service can absorb.
Which is better, full jitter or equal jitter?+
Full jitter is the better default: it spreads retries across the whole window and did the least total work in the widely cited simulation. Equal jitter guarantees at least half the delay, which helps only when a minimum wait is required.
What is decorrelated jitter?+
It is a variant where each wait is drawn between the base delay and three times the previous wait, then capped. Delays still grow on average, but each client's sequence drifts independently of the attempt number.
Should I add jitter when retrying WhatsApp error 130429?+
Yes. 130429 means the number's throughput was exceeded, often by many workers at once, so synchronised retries would hit the limit again. Retry with jittered backoff and fix the pacing so the limit is not reached in the first place.

See InfiQ pricing

Put Backoff Jitter to work

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