API rate limits explained: why you get 429 and how to avoid it

How API rate limits work, what 429 Too Many Requests and Retry-After mean, common limiting algorithms, and how to build clients that stay within limits.

Illustration for the article: API rate limits explained: why you get 429 and how to avoid it

Almost every public API limits how many requests one client can send in a given period. Exceed the limit and the API answers 429 Too Many Requests, or in some cases temporarily blocks your key. Rate limits are not a punishment: they keep a shared service fast and fair for everyone, protect it from bugs and abuse, and keep its costs predictable.

This guide explains how rate limits work, how to read them from response headers, and how to write clients that stay within them instead of fighting them.

Why APIs limit requests

  • Fairness. One busy client should not slow the service down for everyone else.
  • Stability. A bug that sends requests in an endless loop is stopped before it overloads the database.
  • Security. Limits make password guessing, scraping and credential-stuffing attacks slow and expensive.
  • Business plans. Free tiers get lower limits than paid ones; the limit is often part of what you pay for.

What is actually counted

Limits are usually applied per API key, per user account, per IP address, or a combination. Many APIs also set different limits for different endpoints: reading data may allow hundreds of requests per minute, while placing orders, sending emails or logging in allows far fewer. Some services count weight instead of requests, so one heavy request that returns a large list “costs” more than a simple one. Always check the provider’s documentation: two endpoints of the same API can have very different budgets.

Common rate-limiting algorithms

Fixed window

The simplest approach: for example, 100 requests per calendar minute. The counter resets at the start of each minute. It is easy to understand, but a client can send 100 requests at 12:00:59 and another 100 at 12:01:00, creating a burst of 200 in two seconds.

Sliding window

The service looks at the last 60 seconds from the current moment, not the calendar minute. Bursts at window boundaries disappear and the limit behaves more smoothly.

Token bucket

Imagine a bucket that holds, say, 20 tokens and is refilled at 5 tokens per second. Each request takes a token; when the bucket is empty, requests are rejected until it refills. This allows short bursts up to the bucket size while enforcing a steady average rate. It is one of the most widely used algorithms.

Leaky bucket and queues

Requests enter a queue that is processed at a fixed rate. Instead of rejecting extra requests immediately, the service delays them, and only rejects when the queue is full.

Reading rate-limit headers

Well-designed APIs tell you where you stand in every response. Header names differ between providers, but they usually look like this:

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1760880000
  • Limit – the size of your budget for the window.
  • Remaining – how many requests you still have.
  • Reset – when the budget refills, as a Unix timestamp or as seconds from now.

When the limit is exceeded, the response typically looks like this:

HTTP/1.1 429 Too Many Requests
Retry-After: 30

Retry-After is a standard header: either a number of seconds or a date. If it is present, wait at least that long. A newer standard draft introduces RateLimit and RateLimit-Policy headers, so you may see those on modern APIs as well.

How to stay within the limits

1. Respect Retry-After and back off

When you receive 429 or a temporary 5xx error, do not retry immediately. Use exponential backoff with jitter: wait 1 second, then 2, 4, 8 and so on, adding a small random delay so that many clients do not retry at the same instant.

async function callWithRetry(fn, maxAttempts = 5) {
  for (let attempt = 1; ; attempt++) {
    const res = await fn();
    if (res.status !== 429 && res.status < 500) return res;
    if (attempt === maxAttempts) return res;
    const retryAfter = Number(res.headers.get('retry-after'));
    const backoff = Math.min(2 ** attempt * 500, 30000);
    const wait = (retryAfter > 0 ? retryAfter * 1000 : backoff) + Math.random() * 250;
    await new Promise(r => setTimeout(r, wait));
  }
}

2. Cache what does not change

If a value changes once an hour, do not request it every second. Store responses locally and reuse them; use ETag and If-None-Match where the API supports them, because a 304 Not Modified answer is often cheaper or free.

3. Batch and paginate sensibly

Many APIs accept several IDs in one request or return up to hundreds of items per page. Ten requests for ten items can usually become one.

4. Prefer push over polling

Instead of asking “has anything changed?” every few seconds, use webhooks or a streaming connection (WebSocket) when the provider offers them. You get updates instantly and spend almost no request budget.

5. Throttle on your side

If your application sends requests from many places, put them through one small queue or rate limiter that keeps the pace below the provider’s limit. It is far easier than recovering from a temporary ban.

6. Monitor remaining budget

Log the Remaining header or count your own requests. A steady decline toward zero is an early warning before users start seeing errors.

Mistakes that lead to bans

  • Retrying in a tight loop after a 429, which only extends the block.
  • Running several copies of the same script with one API key, each assuming it has the full limit.
  • Rotating IP addresses or creating many free accounts to bypass limits. This usually breaks the provider’s terms of service and can get every key closed.
  • Ignoring per-endpoint limits and treating the global number as the only rule.

If you run an API yourself

  • Return 429 with Retry-After, not a generic 400 or 500.
  • Include limit and remaining headers so clients can adapt.
  • Apply stricter limits to sensitive endpoints such as login, password reset and payments.
  • Document the limits clearly; good documentation prevents most accidental overload.

Key takeaways

  • A 429 response means “slow down”, not “something is broken”.
  • Read the limit headers and honour Retry-After.
  • Retry with exponential backoff and jitter, and cap the number of attempts.
  • Cache, batch and use webhooks to need fewer requests in the first place.

Related reading: HTTP status codes explained, REST API methods and what API services are.