Guides

7 min readUpdated

429 Too Many Requests: what it means and how to fix it

Quick answer

HTTP 429 Too Many Requests means a server, or a service in front of it, has rate-limited you: the same client sent more requests than allowed in a period. It is temporary. Visitors should wait and stop reloading. API clients should honour the Retry-After header and back off with jitter. Site owners should answer per-client limits with 429 and Retry-After rather than 503, and tune limits per route so real users are not caught.

01What does error 429 mean?

RFC 6585 defines 429 as the status that indicates "the user has sent too many requests in a given amount of time". The response should explain the condition and may include a Retry-After header saying how long to wait. The specification deliberately leaves open how the server identifies the user and how it counts: per address, per account, per API key or per cookie, for one URL, a whole site or a group of servers.

Two rules matter in practice. A 429 must not be stored by a cache, so a CDN should never replay one to other visitors. And it is a statement about one client, not about the service: when the whole service is overloaded, 503 is the right code. Not every service follows the convention; GitHub's REST API, for example, answers rate-limited requests with either 403 or 429. Cloudflare's "error 1015" page is a branded 429, covered in the error 1015 guide.

02Is it a 429, a 503 or a challenge page?

The three get confused because all of them say, in effect, not now. Read the status code first, then the headers and the body.

What you receivedWhat it meansHow to recognise itWhat to do
429 Too Many RequestsThis client exceeded a limitOften Retry-After; a short text or JSON bodyWait as long as Retry-After says, then slow down
503 Service UnavailableThe whole service cannot cope right now, or is in maintenanceMay carry Retry-After; affects everyoneRetry later; reducing your own rate will not help
503 from an nginx rate limitA per-client limit using nginx's default status"limiting requests" in the server's error logOwners: set limit_req_status 429
A challenge pageA bot check wants a browser to prove itselfAn HTML page instead of the expected content; Cloudflare marks it with cf-mitigated: challengeOpen it in a normal browser; API clients should stop and report it, not retry
403 from an API with rate-limit headersSome APIs signal rate limits with 403Headers such as x-ratelimit-remaining: 0Follow the API's documented reset time

03What does "Edge: Too Many Requests" mean?

This message has been widely reported on Yahoo and AOL sign-in pages: a Microsoft Q&A thread collects reports from Edge and Chrome users between October 2025 and January 2026, and Mozilla's bug 1992192 records it inside the Thunderbird mail client during Yahoo account setup. Despite the wording it has nothing to do with the Microsoft Edge browser: it comes from the service's own servers, its edge, which are most likely limiting sign-in attempts from your connection.

There is nothing to repair on your computer. Stop retrying, wait before signing in again, and check that no mail app or phone with an old password keeps trying to log in in the background, because those attempts keep the limit tripped.

04How to fix a 429 error as a visitor

  • Wait: if the page shows a time or the response carries Retry-After, wait at least that long
  • Stop reloading, and close other tabs that are polling the same site
  • Turn off browser extensions that prefetch links, auto-refresh pages or check prices in the background
  • Check other devices and apps on your connection: a script, a mail client or a sync tool can use up the limit you share with them
  • Many people can share one public address, for example in an office or behind a mobile carrier; if the error keeps coming back, tell the site operator, who can see why the limit triggers

05How API clients should handle 429: Retry-After and backoff

Honour Retry-After first. RFC 9110 allows two forms: a number of seconds, as in Retry-After: 120, or an HTTP date, as in Retry-After: Fri, 31 Dec 1999 23:59:59 GMT. When the header is missing, wait with exponential backoff and random jitter, so that many clients that failed together do not retry together. An analysis on the AWS Architecture Blog found that full jitter, a random wait between zero and the exponential delay, substantially outperforms plain exponential backoff.

The function below does both. In a test against a local server, it waited 1 second twice after Retry-After: 1 and then succeeded, waited until the date given in an HTTP-date Retry-After, used short jittered waits when the header was absent, and returned a Retry-After: 3600 response at once instead of sleeping for an hour.

Retry only requests that are safe to repeat, such as GET, or POSTs protected by an idempotency key the API supports. Spend the budget evenly: an API that allows 100 requests per 60 seconds allows one every 0.6 seconds, so four parallel workers should each send at most one every 2.4 seconds. Many APIs announce their limits in x-ratelimit headers, and an IETF draft (revision 11, May 2026) is standardising RateLimit and RateLimit-Policy fields for the same purpose. From the command line, curl --retry treats 429 as transient and, since version 7.66.0, follows Retry-After; without the header, curl 7.88.1 waited 1, 2 and then 4 seconds in the same test.

Retry-After plus full-jitter backoff (tested on Node.js 22 against a local 429 and 503 server)
// Retry after 429 or 503: honour Retry-After, else exponential backoff with full jitter.
// Node.js 18+ or a modern browser. Retry only requests that are safe to repeat.
export async function fetchWithBackoff(url, init = {}, { retries = 5, baseMs = 500, maxWaitMs = 60_000 } = {}) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, init);
    if ((res.status !== 429 && res.status !== 503) || attempt >= retries) return res;

    const ra = res.headers.get("retry-after")?.trim();
    let waitMs;
    if (ra && /^\d+$/.test(ra)) waitMs = Number(ra) * 1000; // Retry-After: 120
    else if (ra && !Number.isNaN(Date.parse(ra))) waitMs = Date.parse(ra) - Date.now(); // HTTP date
    else waitMs = Math.random() * Math.min(maxWaitMs, baseMs * 2 ** attempt); // full jitter

    if (waitMs > maxWaitMs) return res; // asked to wait longer than this client allows
    await res.body?.cancel(); // release the connection before sleeping
    await new Promise((r) => setTimeout(r, Math.max(0, waitMs)));
  }
}

06How site owners should send a 429 (nginx example)

nginx's limit_req module rejects with 503 unless told otherwise, so set limit_req_status 429, and add Retry-After through a named error location because nginx does not send it by itself. In the test below, with rate=5r/s and burst=10 nodelay, 20 requests fired at once produced 11 successes and then 429s, the first request plus the burst, and the bucket refilled at five per second. Each rejection was logged at error level as "limiting requests, excess: ... by zone "api"".

The zone size is a short calculation: nginx documents that 1 MB holds about 8,000 states of 128 bytes on 64-bit platforms, so a 10 MB zone tracks roughly 80,000 client addresses.

Two traps are worth knowing. A location that answers with return is never rate-limited, because return runs in the rewrite phase and limit_req in the later preaccess phase: in the test, the same limit in front of return 200 let all 20 requests through. And behind a CDN or reverse proxy, $binary_remote_addr is the proxy's address unless the real client address is restored first, so every visitor would share one counter.

Per-client limit answering 429 with Retry-After (tested with nginx 1.22.1)
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;

server {
    listen 80;
    server_name api.example.com;

    location /api/ {
        limit_req zone=api burst=10 nodelay;
        limit_req_status 429;
        error_page 429 = @too_many;
        proxy_pass http://127.0.0.1:9000;   # your application
    }

    location @too_many {
        default_type application/json;
        add_header Retry-After 2 always;
        add_header Cache-Control "no-store" always;
        return 429 '{"error":"rate_limited","retry_after":2}\n';
    }
}

07Designing limits that do not catch real users

A good limit is invisible to normal use. Measure before enforcing: nginx's limit_req_dry_run (version 1.17.1 and later) counts excess requests without rejecting them, so the log shows who would have been limited. The choice of algorithm and key is covered in depth in what rate limiting is.

EndpointCount perWhyWhen exceeded
Login and password resetAccount and addressSlows password guessing without locking out a whole office429 with Retry-After
API with keysAPI keyFair use per customer, independent of their address429 with rate-limit headers
Search and other expensive pagesSession or addressProtects the database429, or a slower response
Static files and cached pagesUsually no limitThe cache absorbs them, and one page load fetches dozensNot applicable
Partners and webhooksTheir credentialKnown senders with known volumesAgreed with the partner

08What 429s cannot do: DDoS floods and search crawlers

A 429 stops only small floods. A per-client limit stops one noisy client, but a botnet spreads an HTTP flood across thousands of addresses that each stay under the limit, and every rejected request still costs the server a connection and a log line. Rate limits belong alongside filtering in front of the origin, not instead of it.

Mind search engines too. Google treats 429 like a server error and slows its crawling, so a limit that keeps answering Googlebot with 429 reduces how often the site is crawled.

Questions

Frequently asked questions

As long as the server's limit window, which varies from seconds to an hour or more. If the response includes Retry-After, that is the server telling you exactly how many seconds to wait, or until which time. Without it, wait a minute or two before trying again, and do not reload in the meantime, because every attempt counts against the limit and can extend the block.

A 429 is about you: this client sent too many requests and must slow down, while other users are served normally. A 503 is about the server: it cannot handle requests right now, because of overload, maintenance or a failed dependency, and it affects everyone. Both may carry Retry-After. Some servers, including nginx by default, answer rate limits with 503 unless configured otherwise.

Usually, because a rate-limited request is normally rejected before it is processed, but the specification does not guarantee that. Retry GET and other read requests freely after waiting. Retry requests that change data, such as payments or orders, only if the API supports idempotency keys or documents that rejected requests are not executed. Otherwise check the result before sending the request again.

The limit may count more than you. Everyone behind the same public address, such as an office network or a mobile carrier, can share one counter, and apps or extensions on your devices may be making requests in the background. Some services also limit sign-in attempts per account. Waiting usually clears it; if it keeps happening, the site operator can see what triggers the limit.

It can if crawlers receive it. Google treats 429 as a sign that the server is overloaded and temporarily slows its crawling, the same as for 5xx errors. Occasional 429s are harmless, but limits that keep answering search engine crawlers with 429 reduce how often your pages are fetched. Verify crawlers by their documented methods and give them their own, higher limit if needed.

Sources

Sources and verification

The nginx 1.22.1 limit configuration, including the return-phase pitfall, was run in an isolated network namespace and exercised with curl 7.88.1; the backoff function ran on Node.js 22.23 against a local test server returning 429 and 503 with and without Retry-After; curl 7.88.1 --retry timings were measured against the same server. Debian 12, October 2026.

  1. 01RFC 6585, section 4: 429 Too Many Requests · RFC Editor
  2. 02RFC 9110, section 10.2.3: Retry-After · RFC Editor
  3. 03Module ngx_http_limit_req_module · nginx.org
  4. 04nginx development guide: HTTP phases · nginx.org
  5. 05Exponential Backoff and Jitter (Marc Brooker, 2015) · AWS Architecture Blog
  6. 06curl man page: --retry · curl
  7. 07RateLimit header fields for HTTP (Internet-Draft) · IETF Datatracker
  8. 08Rate limits for the REST API · GitHub Docs
  9. 09Detect a Challenge Page response · Cloudflare docs
  10. 10Bug 1992192: Yahoo account creation failure, Edge: too many requests · Mozilla Bugzilla
  11. 11Edge: Too Many Requests (user reports, October 2025 to January 2026) · Microsoft Q&A
  12. 12How HTTP status codes affect Google's crawlers · Google Search Central

Last updated