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.

AlgorithmBurst BehaviorMemory RequirementTypical Use
Token BucketBursts allowed up to the bucket depthLow (counter + timestamp per partition)API gateways, default choice
Leaky Bucket (Queue)No burst, steady output rateLow to medium (queue)Traffic shaping, steady throughput
Fixed WindowUp to double the limit at the window boundaryVery low (one counter per window)Simple quotas, non-critical cases
Sliding Window (Counter)Smooths the boundary effect, close to an exact windowLow (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 a Retry-After header.
  • Retry-After (RFC 9110): communicates how long to wait, either as delay-seconds (e.g. Retry-After: 120) or as an HTTP-date. It is also used with 503 and 3xx.

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 (quota q, time window w in seconds).
  • RateLimit: reports the currently available quota, e.g. RateLimit: "default";r=50;t=30 (remaining r, effective window t).

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

Keep reading