HTTP Errors

What Is the 429 Status Code? (429 Too Many Requests)

What Is a 429 Error (Too Many Requests)? — conceptual illustration
On this page

HTTP 429 Too Many Requests is the status code a server returns when a client has sent more requests in a given window than the server's rate limit allows. A rate limit is simply a cap on how many requests you're allowed to make in a set period of time. The response often includes a Retry-After header — a hint that tells the client how long to wait before trying again. For web scrapers, 429 is the most common form of soft block: the server hasn't decided you're a bot, it's just telling you that you're hitting it too fast and need to slow down.

Quick facts

Status code429
Category4xx Client Error
Standard headerRetry-After (seconds or HTTP date)
Common causesBurst traffic, per-IP rate limits, API quota exhaustion
Right responseBack off, honor Retry-After, slow request rate

What triggers a 429

Servers set rate limits to protect themselves from abuse and from accidentally being overwhelmed (a denial-of-service, where so many requests pile up that the server can't serve anyone). A 429 is usually triggered by one of three things: too many requests per second from a single IP address, too many requests per minute or hour against one specific URL (endpoint), or using up a per-account quota on an authenticated API. The cutoff point varies wildly — a public API might allow 60 requests per minute, while a protected page might block you after just 10 per minute or fewer. Services like Cloudflare and AWS WAF, and most CDNs (the networks that cache and deliver website content), come with rate-limiting rules built in, so even sites that wrote no custom logic can still serve 429s. Scrapers most often hit them when they launch many workers in parallel without coordinating the total request rate across all of them.

How to read a 429 response

Always check the Retry-After header first. It's either a number of seconds (Retry-After: 30) or a specific date and time (Retry-After: Wed, 26 May 2026 14:30:00 GMT). Honor whatever it says. If the header is missing, fall back to exponential backoff — wait 1s, then 2s, then 4s, doubling each time, capping at a few minutes. Look at the response body too: some APIs include a JSON object listing the current limit, how much quota you have left, and when it resets, which is far more useful than guessing. Keep logs of when you got 429s, against which endpoint, and from which IP — that record is the data you'll need to tune how many requests you send at once (your concurrency).

How scrapers handle 429 correctly

The wrong answer is to retry instantly or switch IPs and keep hammering — that turns a gentle rate limit into a hard ban. The right answer has three parts. First, slow your request rate per IP: if you're seeing 429s at 5 requests per second, drop to 2, and slow down further if they keep coming. Second, spread the load across more IPs using proxy rotation (cycling through a pool of IP addresses), but make each IP's pace look human — one request every few seconds, not one every millisecond. Third, queue and retry with backoff: a failed request goes to the back of the line with a delay based on Retry-After plus a little randomness (jitter), so your retries don't all fire at the same instant. Production scrapers treat 429 as routine and plan for it, rather than treating it as an error worth alerting on.

How to fix a 429 in Python (requests)

The fix has three layers: honor Retry-After, back off exponentially with jitter when it's missing, and only then spread load across proxies. The function below does all three. It parses Retry-After whether the server sends seconds (Retry-After: 30) or an HTTP date, waits the right amount, and caps total attempts so a hard block doesn't loop forever.

The most common mistake — and the reason a lot of "too many requests python" searches end in frustration — is a bare time.sleep(5) retry loop that retries instantly on the next 429 and gets the IP banned. Adaptive backoff plus a realistic User-Agent (the default python-requests/2.x header is itself a rate-limit trigger on many sites) fixes the majority of cases. See the full snippet in the code example below.

If 429s persist after backoff the limit is probably per-IP, not per-account. At that point you need rotating proxies so each IP stays under the threshold — or a managed API that pools IPs and paces requests for you.

Code example

python
import time, random, requests
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone

HEADERS = {  # a real browser UA — the default python-requests UA invites 429s
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/124.0 Safari/537.36",
    "Accept": "text/html,application/json",
    "Accept-Language": "en-US,en;q=0.9",
}

def retry_after_seconds(resp):
    """Honor Retry-After whether it's seconds or an HTTP date."""
    ra = resp.headers.get("Retry-After")
    if not ra:
        return None
    if ra.isdigit():
        return int(ra)
    try:
        when = parsedate_to_datetime(ra)
        return max(0, (when - datetime.now(timezone.utc)).total_seconds())
    except (TypeError, ValueError):
        return None

def get_with_backoff(url, max_retries=5):
    session = requests.Session()
    session.headers.update(HEADERS)
    for attempt in range(max_retries):
        resp = session.get(url, timeout=30)
        if resp.status_code != 429:
            return resp
        wait = retry_after_seconds(resp)
        if wait is None:                       # no header -> exponential backoff
            wait = (2 ** attempt) + random.uniform(0, 1)  # + jitter
        print(f"429 received; waiting {wait:.1f}s (attempt {attempt + 1})")
        time.sleep(wait)
    raise RuntimeError("Still rate-limited after retries -- rotate proxies")

resp = get_with_backoff("https://example.com/api")
print(resp.status_code, len(resp.text))

Related terms

What Is the 403 Status Code (403 Forbidden Error)?
HTTP 403 Forbidden means the server understood your request but refuses to answer it. The difference from 401 is simple: 401 means "we don't…
What Is the 503 Status Code (503 Service Unavailable Error)?
HTTP 503 Service Unavailable means the server can't handle your request right now — usually because it's overloaded, under maintenance, or d…
What Is the 499 Status Code (499 Error)?
HTTP 499 Client Closed Request is a non-standard status code, logged by Nginx (and CDNs like Cloudflare) when the client closes the connecti…
What Is Cloudflare Error 1015?
Cloudflare error 1015 "You are being rate limited" means a website is blocking you because you sent too many requests too quickly. The site …
What Is Proxy Web Scraping?
Proxy web scraping means sending your scraper's traffic through proxy servers — middleman machines that forward your requests for you — so t…
What Is a 200 Status Code?
HTTP 200 OK is the standard "success" status code: the server got your request, handled it, and sent back the response you expected. For a G…
What Is a 402 Error?
HTTP 402 Payment Required is the status code a server sends to say: "I won't do this until a payment, billing, or quota problem is fixed." I…
What Is Cloudflare Error 521?
HTTP 521 Web Server Is Down is the error Cloudflare shows when it cannot reach the website's actual server. Cloudflare sits in front of many…
What Is Cloudflare Error 1020 (Access Denied)?
Cloudflare Error 1020 "Access Denied" means a Cloudflare firewall (WAF) rule on the site has blocked your request outright. Unlike Error 101…
What Is Rate Limiting?
Rate limiting is a control that caps how many requests a single client can make to a server within a fixed time window. A site might allow 6…
What Are Request Retries?
Request retries are the practice of automatically re-sending an HTTP request that failed, instead of giving up on the first error. Networks …

Concept map

How 429 Status Code (429 Error) connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · HTTP Errors
Building map…

Frequently asked questions

What's the difference between 429 and 503?

429 is specifically about how fast the client is sending requests. 503 means the server itself is unavailable — overloaded, mid-deployment, or down. Both can come from rate-limit middleware, but 429 is the one that correctly says "you're going too fast."

Does using a proxy fix 429s?

It can. If the limit is per-IP, rotating through different IPs spreads your requests out so each IP stays below the threshold. But if the limit is tied to your account or your fingerprint (the unique profile a server builds from your connection), swapping IPs alone won't help. Figure out which kind of limit you're hitting before throwing more infrastructure at it.

How long should I wait after a 429?

Honor <code>Retry-After</code> if it's present. Otherwise the standard approach is exponential backoff: start at 1 second and double each time (1s, 2s, 4s...), adding a bit of random jitter so retries don't bunch up. Cap the wait at 5–10 minutes; if you're still getting 429s after that, you're no longer just rate-limited — you're banned.

Can a 429 turn into a permanent ban?

Yes. Repeatedly triggering 429s from the same IP is a strong bot signal, and many sites escalate it into long-term IP blocks. Treat a 429 as a cue to slow down, not as a free allowance to keep retrying.

How do I fix a 429 error in Python?

Read the Retry-After header and sleep for exactly that long; if it is absent, use exponential backoff with jitter (1s, 2s, 4s… plus a random fraction). Send a real browser User-Agent instead of the default python-requests one, and use a requests.Session so connections and cookies are reused. If 429s continue, the limit is per-IP — rotate proxies so each IP stays under the cap.

Why do I get 429 with python-requests but not in my browser?

Two reasons. The default User-Agent (python-requests/2.x) is an obvious bot signal that many sites rate-limit harder, and scripts fire requests far faster than a human clicks. Set browser-like headers and add delays, or route through a scraping API that handles pacing and fingerprints.

Last updated: 2026-06-08