IntegrAuth Academy
Identity, security and practical AI — free, from zero. Byte-sized lessons, real diagrams, hands-on labs, zero fluff. Two programs, each with its own exam and certificate.
English is hard? Your browser can translate this page into Tamil, Telugu, Kannada, Malayalam or Hindi. Tech words stay in English so you learn the real names.
How?
- Chrome on phone: ⋮ menu → Translate
- Chrome on computer: right-click → Translate to …
- Edge: the translate icon in the address bar
Rate limits, quotas & abuse control
One badly behaved client can flood an API until it falls over for everyone. It might be a buggy retry loop, a scraper or a password-guessing script. Your goal: set limits that stop abuse, and answer callers politely when they hit one.
- Limits protect availability, cost and security.
- Choose what to count: IP, key, user, tenant or account. Layer them.
- A token bucket allows short bursts and keeps a steady average.
- Answer with 429 and Retry-After. Clients add jitter.
- A rate limit caps speed. A quota caps volume.
Why limit at all
a tea stall that serves about ten people a minute. When fifty push in, the owner does not shut for ever. He says "wait two minutes and come back". That is a rate limit with a polite answer. "One litre of milk per family per day" is a quota.
- Availability. One client cannot use up the servers.
- Cost. An API with no bounds has a bill with no bounds (see unrestricted resource consumption).
- Security. Limits slow down password guessing and credential stuffing (see credential stuffing, password spraying and bots).
What do you count, and for whom?
A limit says "so many requests per something per time unit". The something matters as much as the number.
- Per IP address. All you have for anonymous traffic, but blunt. A whole office can share one address. An attacker can spread over thousands.
- Per API key or client. Fair between applications.
- Per user. One account cannot starve others.
- Per tenant. One customer must never slow another (see multi-tenancy).
- Per account on login. This catches guessing aimed at a few accounts from many addresses.
Real systems stack several. A loose per-IP limit at the edge absorbs floods. Per-account and per-tenant limits sit inside. A single global limit is bad, because one caller can starve everyone.
Four ways to count
| Method | How it works | Trade-off |
|---|---|---|
| Fixed window | Count requests in each clock window, such as each minute. Reset at the boundary. | Simple. But 100 requests at 12:00:59 and 100 more at 12:01:00 all pass. That is double the limit in a second. |
| Sliding window | Count over the last 60 seconds, not by clock minute. | Removes the boundary spike. Costs a little more. |
| Token bucket | A bucket holds up to b tokens. It refills at a steady rate r. Each request spends a token. Empty means refuse. | Allows bursts up to b. Keeps the long-run average at r. |
| Leaky bucket | Requests queue and drain at a fixed rate. Overflow is dropped. | Smooth output. Can add delay. |
Example: b = 10 and r = 2 per second. A client sends 10 requests at 0 s. The bucket is empty. After 2 seconds, 4 tokens are back. So of 6 more requests, 4 pass and 2 are refused.
Saying no politely: 429 and Retry-After
When a caller is over the limit, answer with HTTP 429 Too Many Requests. It means "not right now". Send a Retry-After header with the seconds to wait. A good client obeys it. If the retries still fail, it waits longer each time. It also adds a random bit of wait (jitter). Then clients do not all rush back together. See cost, rate limits and prompt caching.
A rate limit caps speed, such as requests per minute. A quota caps volume over a long time, such as 100,000 calls a month. A caller can be under the rate limit and still be out of quota. A header like RateLimit: "default";r=0;t=30 describes the current window, not a monthly quota.
Return 429, not 403, to a caller over the limit. A 403 means "never", so a client gives up. Always send Retry-After. Without it, polite clients guess and rush back when the limit lifts.
Go deeper: Telling the client its budget
Many services use non-standard headers such as X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Each provider spells them differently. RFC 6585 defines 429 and says a cache must not store it. RFC 9110 says Retry-After is seconds or an HTTP date.
An IETF Internet-Draft, draft-ietf-httpapi-ratelimit-headers, is still a draft. As of October 2026 its latest version is 11 (May 2026). It defines RateLimit-Policy, a quota q over a window w, such as "default";q=100;w=60. It also defines RateLimit, the quota left r and seconds left t. If a reply has both RateLimit and Retry-After, Retry-After wins. The draft may still change.
Go deeper: Beyond a simple counter
- Runaway retries. A sync job that retries instantly, with no cap, can make millions of calls overnight and lock out live traffic. Back off, add jitter, cap the retries, and give batch keys their own gentler limit.
- Many servers. A count on each server multiplies the real limit. Keep the counter in one shared fast store, or accept some slack.
- Idempotency keys. A client that retries a POST after a timeout may create two of something. Let it send a unique idempotency key. The server returns the first result for any repeat.
- Cap the size of one request. Set a maximum page size and reject oversized bodies (resource consumption).
- Cost limits. A flexible query can do the work of thousands of requests. Give each query a cost score and cap the total.
Go deeper: Flexible query APIs (GraphQL)
GraphQL lets a caller choose the fields in one request. Main risks:
- Deep or wide queries. One request can do the work of thousands. Limit depth and cost.
- Batching and aliases. Hundreds of operations in one request defeat "one login try per request". Limit operations per request.
- Introspection. A caller can ask the server to describe all its types. Turn it off in production for private APIs.
- Permissions per field. Check permissions on every object and field (the OWASP API tour).
🧪 Interactive lab — enable JavaScript to play with this one.
The GitHub REST API sends X-RateLimit-* headers on every reply. GET https://api.github.com/rate_limit shows your budget without spending it. Watch "remaining" fall as you call. Then read the RateLimit header fields draft.