TroubleshootingTroubleshooting

429 and retry behaviour

How rate limiting works and how to back off correctly.

By the end: You can handle 429 responses with the right delay and jitter.

A 429 means the request was refused before reaching a model, so it costs nothing.

Reading the response

FieldValueNotes
retry-afterSecondsWait at least this long before retrying.
error.coderate_limit_exceededA rate or concurrency limit was hit.
error.codequota_exceededA daily or monthly quota is exhausted; retrying soon will not help.

Backoff that works

Pattern

let attempt = 0;
for (;;) {
  const res = await send();
  if (res.status !== 429) return res;

  const wait = Number(res.headers.get('retry-after') ?? 2 ** attempt);
  const jitter = wait * 0.25 * Math.random();
  await new Promise((r) => setTimeout(r, (wait + jitter) * 1000));

  if (++attempt > 5) throw new Error('still rate limited');
}
Retrying quota_exceeded immediately just burns time. It resets at the start of the next UTC day or month.