Two earlier pieces on this site covered circuit breakers and backpressure, and both were ultimately about the same question asked at different points in time: can this system currently handle what's being asked of it. A breaker answers that after a dependency has already started failing. Backpressure tries to answer it continuously, before failure, by making a real-time capacity signal travel backward through a chain. Rate limiting looks, at a glance, like a third variation on the same theme — another mechanism that says no when there's too much traffic — and that resemblance is exactly what makes it worth separating out carefully, because the question it actually answers is a different one. A rate limit doesn't ask whether the system can handle a request right now. It asks whether this particular requester is allowed to make it, a policy decision that holds even when the answer to the capacity question is an easy yes.
An API that could comfortably serve ten thousand requests a second in aggregate will still cap a single free-tier client at, say, sixty requests a minute, even at three in the morning when the service is otherwise idle and has capacity to spare. That's the detail that separates rate limiting from everything discussed in the two earlier pieces: it isn't triggered by the system struggling. It's a number set in advance, attached to an identity — an API key, a user account, an IP address, a tenant — and enforced regardless of how much headroom actually exists at the moment a request arrives. The reasons behind that number are rarely about capacity at all. They're about fairness among many clients sharing one resource, so that one client's burst of activity can't degrade service for everyone else. They're about cost, when each request has a real marginal expense the operator is choosing not to absorb without limit. They're about the terms of a contract — what a given pricing tier is entitled to — or about abuse prevention, where the limit exists specifically because a client's behavior looks less like legitimate use and more like scraping or credential stuffing. None of those are questions about whether the system is currently able to do the work. They're questions about who gets to ask, and how much, decided ahead of time as policy rather than sensed in real time as load.
The simplest implementation is a fixed window counter: pick a window — one minute, say — and count requests from a given identity within it, resetting the counter to zero the instant the window rolls over. It's cheap and easy to reason about, and it has one well-known flaw: a client can send its full quota in the last second of one window and its full quota again in the first second of the next, producing twice the intended rate over that two-second span while never once exceeding the stated limit inside any single window. A sliding window log fixes this by recording the timestamp of every request and counting how many fall within the trailing window at the moment of each new one — exact, but it costs memory proportional to the number of requests a busy identity can make. A sliding window counter is the practical compromise: keep counts for the current and previous fixed windows and weight them by how far into the current window the clock has moved, producing an estimate close to the exact sliding-log answer at a small fraction of the storage cost.
The two mechanisms most real systems actually reach for work on a different principle: a rate over time rather than a count per window, which sidesteps the boundary problem entirely. A token bucket holds a fixed number of tokens, refilled at some steady rate — one token every 100 milliseconds, say — and every request consumes one token, succeeding if any are available and failing or queuing if the bucket is empty. Because the bucket can hold up to its full capacity at once, a client that's been idle can spend a genuine burst — use several seconds' worth of saved-up tokens all at once — while a client that's been steadily busy is held to the steady refill rate with nothing saved up to spend. That's a deliberate, useful asymmetry: it distinguishes idle-then-bursty traffic, which is often perfectly legitimate, from sustained high-rate traffic, which is exactly what the limit exists to cap. A leaky bucket enforces the same steady rate from the other direction: incoming requests fill the bucket instead of draining it, and the bucket leaks out — is processed — at a constant rate regardless of how fast it filled, with anything that arrives when the bucket is already full simply overflowing and being dropped. Token bucket is usually framed from the requester's side, deciding whether this request is allowed; leaky bucket is usually framed from the system's side, smoothing an uneven arrival pattern into an even output one. Different framing, close to the same shape underneath.
Backpressure, in the mechanisms the earlier piece described, mostly
operates between parties that already trust each other and share
infrastructure — a TCP sender and receiver, two stages of one internal
pipeline. Rate limiting is applied more often at the boundary of a
system and a party outside its control entirely: a third-party
developer calling a public API, a browser making requests to a server
it has no other relationship with. Because there's no shared internal
signal to lean on, the limit has to be communicated as an explicit,
documented contract instead of an implicit mechanical one. A request
over the limit typically returns HTTP 429, Too Many Requests, distinct
from a 503 precisely because it says "not right now, from you" rather
than "the system is down." A well-behaved API pairs that with headers
stating the limit, how many requests remain in the current window, and
when the window resets, plus a Retry-After value telling
the caller exactly how long to wait before trying again — turning what
could be an opaque rejection into something a client can program
against deterministically, the same spirit as an idempotency key
turning a retry from a guess into a guarantee.
The gap worth being precise about, at the end, is the same gap the opening drew: a rate limit is a promise about policy, and policy can be fully honored while capacity still gives out. If a service sets generous per-client limits and then acquires far more active clients than it planned for, every single one of them can stay strictly under its individual quota while their combined traffic still exceeds what the system can actually process — the limit did exactly its job, enforcing fairness and the agreed terms for each identity, and the system still falls over, because no one asked the different question of whether the sum was sustainable. That's not a bug in rate limiting; it's the boundary of the problem it was built to solve. The aggregate capacity question is exactly what backpressure and circuit breakers exist to answer — backpressure by signaling, in real time, whether the system can currently keep up regardless of whose traffic is whose, and a breaker by containing the failure if it can't. A complete system needs all three, answering three different questions that only look like one from a distance: rate limiting decides, per identity and in advance, how much any single requester is entitled to ask for. Backpressure decides, continuously and for the system as a whole, whether it can currently keep up with what's arriving. A circuit breaker decides what to do once something has already gone wrong despite both. None of the three can stand in for either of the others.