Rate limiter

BackendTier 3 · scale vocabularyPhase 3 · Give customers accounts

A guard that caps how many requests someone can make in a time window, blocking abuse and runaway traffic.

A single client, whether a runaway script, a scraper, or an attacker, can send thousands of requests a second and starve everyone else or drive up cost. A rate limiter exists to cap how many requests a caller may make in a window and reject the rest, protecting the service and its other users.

Without limits, a script could try 1,000 passwords a second, or hammer an endpoint until the server falls over. A rate limiter counts requests per user or per IP and, past a threshold, rejects further ones with a 429 status until the window resets.

It protects against brute-force attacks, abusive scraping, and accidental floods. The limits are tuned: high enough for real users, low enough to stop abuse.

Tap as fast as you can. Predict what happens after the fifth.
window: 5 requests / 10 s
No requests yet.
Each slot is one unit of your budget in the current window. A used slot frees itself ten seconds after its request, and the sixth tap inside the window is refused.

A junior PM, stuck

A partner integrating our API messaged that their orders keep failing with a 429 and asked me, point blank, what our actual limits are. I do not know the number, and I do not want to guess and give a partner the wrong figure in writing.

A 429 is the server saying "too many requests, slow down." A rate limiter caps how many calls a caller gets in a time window and rejects the rest with that code. You do not have to guess the number; it is printed in the response headers. Here is the real pair from our rate-limit route: the last request that was allowed and the one right after that got blocked. The limit and the wait are sitting right there in the headers.

The rate-limit boundary on /api/rate-limit: the last allowed request and the first blocked one
Request 60 in the window, still allowed
1.1
200
: 60
: 0
: 32
Request 61, blocked
1.1
429
: 32
{ "error": "rate_limited" }

Click a step to see the lines it points at.

Guessing the limit for a partner. The exact number is in X-RateLimit-Limit on every response; quoting a number from memory risks putting the wrong figure in a partner contract.
Reading a 429 as an outage. It is the limiter working as designed, not the service falling over; the caller is simply going too fast and needs to back off.
Ignoring Retry-After. A partner whose code retries immediately after a 429 just gets blocked again; the header already tells them the exact seconds to wait.
Assuming raising the limit is the only lever. Sometimes the caller is looping wastefully and the fix is on their side; the headers let you tell an abusive pattern from a genuinely low cap before you change anything.

Reply to the partner: "Our limit is 60 requests per window, and it is in every response as X-RateLimit-Limit. When you hit a 429, read the Retry-After header and wait that many seconds before retrying, rather than looping. If 60 is genuinely too low for your volume, tell me your expected rate and I will raise it with the team." You read the number off the header instead of guessing.

When integrating a partner API, their rate limits shape what you can build; "we'll hit their limit" is a real constraint.
Login rate limiting is basic security; if your product doesn't have it, that's a gap worth raising.

"We're getting 429s from the payment API, we're calling it too often."

Appears in Phase 3, Give customers accounts.