Terms: rate limit, quota, and throttling
The three terms are often conflated, but they describe different aspects of the same idea: limiting request volume.
- Rate Limit: the maximum number of requests in a short, sliding time window, typically per second or per minute (e.g.
100 requests/minute). It protects against overload and smooths load spikes. - Quota: an allotment over a longer period, such as per day or per month (e.g.
10,000 requests/day). Quotas mostly govern business or commercial limits (pricing tiers, fair distribution) rather than acute overload. The IETF draft on RateLimit headers formally defines a quota as the capacity allocated to a partition, measured in quota units within a time window. - Throttling: the actual behavior when a limit is exceeded. The server can reject requests (e.g. with
429), delay them (shaping), or cut the connection. Throttling is thus the enforcement; rate limit and quota are the rules.
In practice, multiple tiers are combined, such as a short per-second burst limit, a per-minute limit, and a daily quota. The IETF draft names exactly this pattern as an example of overlapping policies that can jointly apply to a request.
| Algorithm | Burst Behavior | Memory Requirement | Typical Use |
|---|---|---|---|
| Token Bucket | Bursts allowed up to the bucket depth | Low (counter + timestamp per partition) | API gateways, default choice |
| Leaky Bucket (Queue) | No burst, steady output rate | Low to medium (queue) | Traffic shaping, steady throughput |
| Fixed Window | Up to double the limit at the window boundary | Very low (one counter per window) | Simple quotas, non-critical cases |
| Sliding Window (Counter) | Smooths the boundary effect, close to an exact window | Low (two counters per partition) | More precise limits without a full log |
Rate limiting algorithms compared
Algorithms: token bucket, leaky bucket, fixed and sliding window
A rate limiter needs a counting mechanism. Four methods are common; they differ mainly in how they handle short-term bursts.
Token Bucket
A bucket holds a maximum of N tokens and is refilled at a constant rate. Each request removes one token; if the bucket is empty, the request is rejected. The average rate corresponds to the refill rate, and the bucket depth determines the allowed burst size. The method thus allows controlled load spikes and is the model most commonly used in API gateways (e.g. Amazon API Gateway uses token bucket with a burst parameter).
Leaky Bucket
Mirror image of the token bucket. Requests flow into a bucket (queue) that drains, i.e. is processed, at a fixed rate. If the bucket overflows, requests are dropped. As a queue, leaky bucket enforces a steady output rate (traffic shaping); as a pure meter (a counter that decreases at a fixed rate), it is mathematically equivalent to the token bucket.
Fixed Window
A simple counter per fixed interval, such as per calendar minute. Easy to implement, but it has the boundary problem: at the window boundary, up to two full limits can occur back to back (end of one window, start of the next), letting through double the amount for a short time.
Sliding Window
Refines fixed window by having the window move continuously with time. In practice, a sliding window counter is common, which combines the previous and current window counters with weighting. This smooths the boundary effect at moderate memory cost and comes close to a true sliding log, without needing to store every timestamp.
HTTP signaling: 429, Retry-After, and RateLimit headers
When a limit is exceeded, an API should respond in a machine-readable way, so clients can adjust their behavior instead of blindly continuing to poll.
- Status code:
429 Too Many Requests(RFC 6585) signals that the client has sent too many requests in a given time period. The response SHOULD explain the condition and MAY include aRetry-Afterheader. Retry-After(RFC 9110): communicates how long to wait, either asdelay-seconds(e.g.Retry-After: 120) or as anHTTP-date. It is also used with503and3xx.
For proactively communicating remaining capacity, there was long no unified standard; every implementation named its headers differently. The IETF draft draft-ietf-httpapi-ratelimit-headers (currently version 11, httpapi working group) unifies this with two fields in Structured Fields format:
RateLimit-Policy: describes the static policy, e.g.RateLimit-Policy: "burst";q=100;w=60,"daily";q=1000;w=86400(quotaq, time windowwin seconds).RateLimit: reports the currently available quota, e.g.RateLimit: "default";r=50;t=30(remainingr, effective windowt).
Important: these headers are hints, not a guarantee. The draft explicitly clarifies that they do not serve authorization, and a positive remaining value does not guarantee that the next request will be served. Optionally, on exceeding a limit, the server can deliver an application/problem+json body with the problem type quota-exceeded and the violated policies (violated-policies). If a response contains both Retry-After and RateLimit, Retry-After takes precedence.
Granularity: per client, key, user, and tenant
A global limit across all requests does protect the infrastructure, but it's unfair and easily abused; a single attacker can crowd out legitimate users. Limits should therefore be bound to a partition. The IETF draft calls this a partition key and lists as strategies: per user, per application, per HTTP method, per resource, or combinations thereof.
- Per API key or client application: the usual axis for pricing tiers and quotas. API gateways typically bind limits to a key or a usage plan.
- Per authenticated user: necessary so that clients behind shared IPs (NAT, proxy) don't block each other.
- Per tenant: in multi-tenant systems, a limit per tenant prevents one noisy tenant from consuming the capacity of all others (the noisy neighbor problem).
- Per IP: useful against unauthenticated floods, but unsuitable as the sole axis, because attackers can distribute across many IPs, and legitimate users behind one IP can collapse together.
- Per endpoint/operation: expensive or sensitive operations (login, password reset, search, report export) need stricter limits than cheap read access.
Sensitive data doesn't belong in the partition key, since it can become visible in response headers.
Distributed rate limiting
As soon as more than one instance serves the API, a local in-memory counter is no longer sufficient. If traffic is distributed across k nodes via a load balancer, each node with a local limit L allows up to k * L requests in total, undermining the overall limit.
Approaches:
- Central counter store: a shared, fast store (often Redis) holds the counters. Atomic operations or scripts prevent race conditions during simultaneous decrementing. Cost: additional latency and a shared point of dependency.
- Enforcement at the gateway/edge: rate limiting is moved in front of the instances, to the API gateway or reverse proxy, so counting happens at a central point. The IETF draft explicitly notes that such headers are set by intermediaries such as API gateways.
- Local allotments with reconciliation: each node receives a partial budget and periodically reconciles with a coordinator. Lower latency, but briefly less precise.
Cloud gateways often combine this with regional or account-wide ceilings. Amazon API Gateway, for example, describes its throttle and quota values as best-effort targets, not guaranteed ceilings, a realistic view of distributed enforcement.
Protection against API4, brute force, and credential stuffing
Rate limiting is the direct defensive counterpart to Unrestricted Resource Consumption (API4:2023). OWASP explicitly recommends there limiting how often a client interacts with the API within a time period, and fine-tuning this per endpoint. Especially expensive or billable operations (SMS sending, email, external calls) must additionally be throttled and given spend limits.
Against brute force and credential stuffing, rate limiting works at authentication endpoints, but isn't sufficient on its own. The OWASP Authentication Cheat Sheet recommends multi-layered login throttling:
- Account lockout after a threshold of failed attempts, where the counter should be bound to the account rather than just the source IP, so distributed attacks across many IPs don't slip through.
- Exponential backoff: the lockout duration starts very short (e.g. one second) and doubles after each failed attempt.
- CAPTCHA, ideally only after a few failed attempts, so as not to unnecessarily slow down legitimate users.
- MFA as the most effective single protection against password-related attacks.
Business-logic abuse patterns too (mass account creation, automated bookings) can be contained via limits set per user and per operation. Note that account lockout can itself become a denial of service if attackers deliberately lock out other users' accounts, which is why account-based lockouts should be combined with additional signals.
Sources
- RateLimit header fields for HTTP (draft-ietf-httpapi-ratelimit-headers-11) IETF, httpapi Working Group, 2025
- RFC 6585: Additional HTTP Status Codes (429 Too Many Requests) IETF, 2012
- RFC 9110: HTTP Semantics, Section 10.2.3 Retry-After IETF, 2022
- API4:2023 Unrestricted Resource Consumption - OWASP API Security Top 10 OWASP, 2023
- Authentication Cheat Sheet (Login Throttling, Account Lockout, MFA) OWASP Cheat Sheet Series, 2024
- Throttle requests to your REST APIs for better throughput in API Gateway (Token Bucket, Usage Plans) Amazon Web Services, 2024
- Token bucket Wikipedia, 2024
- Leaky bucket Wikipedia, 2024