429 Too Many Requests means the server is rate limiting you: the client has sent more requests in a period than the server allows, and it should wait before trying again. Nothing is broken, and repeating the request at once makes it worse. A well-behaved server says how long to wait in a Retry-After header; a well-behaved client waits at least that long, and without one backs off with growing delays. When a monitor hits a 429, the usual fix is to exempt the monitor from the limit or check an endpoint without one.
How rate limiting works
RFC 6585 defines 429 but leaves the rules to each server. A limit has three parts:
- Who is counted: an IP address, an API key, a user account, or a combination. Limits by IP count everyone behind the same address together.
- What is counted: all requests, or only some, such as logins, searches or a costly API endpoint, often with a stricter limit.
- How time is counted: a fixed window resets on the minute; a sliding window looks at the last 60 seconds; a token bucket refills at a steady rate and allows short bursts up to its size.
The response often tells you where you stand:
HTTP/2 429
retry-after: 30
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
x-ratelimit-reset: 1793091600
Retry-After is standard and is either a number of seconds or an HTTP date. The X-RateLimit-* headers are a common convention rather than a standard, and their meaning varies: reset may be a Unix time or a number of seconds, so read the API’s documentation. An IETF draft defines standard RateLimit and RateLimit-Policy fields, which some APIs have started to send.
Where 429s come from
- Third-party APIs you call, such as payment, email, maps or chat services, which limit each key or account.
- Your own API gateway or framework, through rate limiting middleware.
- Your web server. nginx’s
limit_reqanswers 503 by default; setlimit_req_status 429so clients know to slow down rather than assume an outage. - CDNs and WAFs, through rate limiting rules. Depending on the rule, the answer is a 429, a 403 or a challenge page.
- Login and form protection in a CMS or security plugin, which limits attempts per address.
Handling a 429 as a client
- Honor Retry-After. Wait at least as long as it says. Retrying sooner usually gets another 429 and can extend a block.
- Back off exponentially, with jitter. Without Retry-After, wait longer after each failure, for example 1, 2, 4 and 8 seconds, plus a random part, so many clients don’t retry at the same moment.
- Cap the retries. After a few attempts, give up and report the error, or put the work in a queue for later.
- Send fewer requests. Cache answers, use batch endpoints, ask for larger pages, and use webhooks instead of polling where the API has them.
- Spread scheduled work. Cron jobs that all start on the hour hit limits together; offset them by a few minutes. The guide to cron expressions shows how.
async function fetchWithBackoff(url, options = {}, attempts = 5) {
for (let attempt = 0; attempt < attempts; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const retryAfter = Number(response.headers.get('retry-after'));
const wait = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 1000 + Math.random() * 1000;
await new Promise((resolve) => setTimeout(resolve, wait));
}
throw new Error('Still rate limited after ' + attempts + ' attempts');
}
This sketch only reads Retry-After as seconds; production code should also accept the date form.
Setting rate limits on your own site
- Answer 429, not 503 or 403. A 503 looks like an outage and a 403 like a permanent refusal; a 429 tells clients and monitoring tools exactly what happened.
- Send Retry-After, so clients don’t have to guess.
- Limit by key or account where you can. Limits by IP punish offices, schools and mobile networks where many people share one address.
- Allow bursts. A page load can make dozens of requests at once; a token bucket with room for a burst avoids blocking normal visitors.
- Exempt your monitoring, or give it its own health check endpoint without a limit.
# nginx: 10 requests a second per address, bursts of 20, answered with 429
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
}
}
Why an uptime monitor can hit a 429
One check a minute sounds harmless, but it adds up in ways that are easy to miss:
- Several monitors on one host. Ten monitors on the same API, each checking every minute, are ten requests a minute from the same client, plus the confirming re-checks when something fails.
- Strict limits on the checked path. Login pages, search and form endpoints often allow only a few requests a minute per address.
- Shared addresses. A monitoring service’s checks come from a few known addresses. A limit by IP counts every check from that address together, including other users’ checks of the same public site or API.
- A bot rule that rate limits. Some firewalls count automated clients separately and limit them harder than browsers.
What to do, in order: exempt the monitor from the limit by its IP addresses, monitor a health endpoint that has no limit, check less often, or combine monitors that hit the same API. Don’t accept 429 as up across the board, since a limit that starts blocking your real users is worth knowing about. HTTP status codes in monitoring covers which codes to treat as failures, and the API monitoring checklist what else to watch on an API.
429s and Uptimecheck
In Uptimecheck uptime monitoring and API monitoring, a 429 is outside the default expected statuses (200 to 399), so it counts as down once a second check confirms it. When the server answers a plain 429, the incident says that our checks are rate limited, quotes the Retry-After value when the server sent one, and suggests exempting requests whose user agent contains “Uptimecheck”, checking less often or using an endpoint without the limit. Checks run from Stockholm, Sweden; how to allow Uptimecheck through a firewall lists the addresses to exempt, with rules for Cloudflare, including its rate limiting rules, and for nginx. You choose each monitor’s interval, from every minute to every hour.
Uptimecheck follows the same rules when it sends your alerts. If Slack, Microsoft Teams, Discord or PagerDuty answers 429, the alert is retried after 30 seconds, 2 minutes, 10 minutes and 30 minutes, and never sooner than the service’s Retry-After asks, up to an hour. See why alerts may not arrive for what happens when a channel keeps failing.
Tell a rate limit from an outage. Try Uptimecheck free for 14 days, no credit card required.