← Back to the library
BackendCore · 7 min

A third-party API starts returning 429

“Batch jobs or a traffic spike hit a provider limit; requests fail with 429, retries make it worse, and the provider may temporarily block the key.”

ERROR CODES YOU MAY SEE

A code is a clue. Use its meaning and surrounding evidence to narrow the cause.

429HTTP status codes · RFC 6585 · standard

The client sent too many requests in a given amount of time (rate limiting). The response may include Retry-After.

Read Retry-After and any provider rate-limit headers before retrying. Check whether the limit is per key, per account, or per endpoint, and how many workers share it.

Official reference for 429 (opens in new tab)Code definition reviewed
503HTTP status codes · RFC 9110 · standard

The server is temporarily unable to handle the request due to overload or maintenance. It may send Retry-After.

Treat it as transient: honor Retry-After when present, otherwise back off with jitter and cap total attempts.

Official reference for 503 (opens in new tab)Code definition reviewed

THE PRINCIPLE

Control your request rate on your side; a 429 means your own limiter is missing or mis-sized.

FIRST MOVES

  1. Log status, Retry-After, and provider rate-limit headers per request and per API key.
  2. On 429 or 503, wait for Retry-After (seconds or HTTP-date) when present; otherwise use capped exponential backoff with full jitter.
  3. Cap concurrency and rate with one limiter shared by all workers using the same key, sized below the quota.
  4. Retry only idempotent requests or ones sent with an idempotency key; stop after a bounded number of attempts.
  5. Move bulk work to a queue so interactive traffic keeps headroom.

TOOLS: THEN → NOW

Fixed sleep-and-retry loopsRetry-After first, then capped exponential backoff with full jitter
Per-process throttlesShared token bucket or queue rate limit per API key

PATTERN SNAPSHOT

function retryDelayMs(res: Response, attempt: number): number {
  const header = res.headers.get("retry-after");
  if (header) {
    const seconds = Number(header);
    const ms = Number.isNaN(seconds) ? Date.parse(header) - Date.now() : seconds * 1000;
    if (ms >= 0) return ms;
  }
  // Full jitter: random(0, min(cap, base * 2 ** attempt))
  return Math.random() * Math.min(60_000, 500 * 2 ** attempt);
}

CLOSE THE AI. EXPLAIN THIS.

Why do ten workers with identical exponential backoff still hammer a recovering API?

HOW IT WORKS UNDERNEATH

SOURCES

Guide reviewed