SaaS

Rate Limiting a SaaS API Without Punishing Real Users

Every public API needs rate limiting, or one bad actor, or one buggy script, can hammer it into the ground. But clumsy rate limiting punishes your best customers along with the abusers. The goal of API rate limiting is to stop abuse while normal use never notices the limit exists.

Abuse isn’t always malicious. A customer’s own script stuck in a retry loop, or a sync job someone set to run every few seconds, can do as much damage as an attacker. Rate limiting is what stops one mistake from becoming everyone’s outage. Unrestricted resource consumption is common enough that it appears in the OWASP API Security Top 10.

Illustration of a web application dashboard, Rate Limiting a SaaS API Without Punishing Real Users
API rate limiting that stops abuse without hurting real customers: which key to limit on, token buckets, 429 and Retry-After, and a safe…

Choose the right key

Limiting by IP alone is blunt: an entire office behind one IP shares the limit, and one heavy user there blocks everyone. For an authenticated API, limit per API key or per account so each customer gets their own budget and one customer can’t affect another.

Unauthenticated endpoints are different. A login, a password reset or a signup has no account to key on yet, so limit by IP there and also by the target, such as the email address being tried. That second key is what slows down a distributed attempt to guess one user’s password. I cover the wider picture in authentication mistakes found in small SaaS apps.

Set limits to real usage, and be forgiving

Look at how your real users behave and set the ceiling well above normal peaks. A limit that a legitimate integration hits during normal work is too low. Different endpoints can have different limits, a cheap read can allow far more than an expensive export. Use a token bucket so short bursts (which real apps do) are allowed while sustained abuse is still capped.

Which API rate limiting algorithm to use

There are three common ways to count, and the choice decides how the limit feels to a real client:

AlgorithmHow it worksThe catch
Fixed windowCounts requests per clock minute (or hour) and resets at the boundarySimple, but a client can send a full budget at the end of one window and another at the start of the next
Sliding windowCounts requests over the last N seconds, whenever you askSmoother, but a little more work to track
Token bucketA bucket refills at a steady rate and each request takes a tokenAllows bursts up to the bucket size and caps the sustained rate; you tune two numbers instead of one

For most SaaS APIs I reach for a token bucket per API key, stored somewhere fast and shared, such as Redis, so every app server sees the same count. Counting in each server’s memory breaks as soon as you run more than one instance, because each server hands the customer a full budget of its own.

Tell clients where they stand

Then communicate. Return the limit, how many requests remain, and when it resets in response headers, and use the standard 429 status with Retry-After when someone is throttled. A developer who can see the limits will design around them; one who gets a silent failure will just be angry.

In practice that means three things:

  • Headers on every response. Many APIs use X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Pick a format, document it, and keep it consistent across endpoints.
  • A proper 429. The 429 Too Many Requests status tells any HTTP client exactly what happened, and Retry-After tells it how long to wait.
  • A readable error body. Say which limit was hit and link to the page in your docs that explains the limits.
Infographic summarising “Rate Limiting a SaaS API Without Punishing Real Users”: Choose the right key; Set limits to real usage, and be forgiving; Which API rate limiting algorithm to use; Tell clients where they stand; Common mistakes; Limits beyond the API; How to roll it out safely
The article at a glance.

Common mistakes

  • Returning 500 or 403 when throttling. The client can’t tell "slow down" from "broken" or "forbidden", so it either gives up or retries harder.
  • One global limit for every endpoint. A limit tight enough to protect an expensive report is far too tight for a cheap status check.
  • Throttling your own front end. A dashboard that fires many requests on load can trip limits designed for third-party scripts. Give first-party traffic its own budget, or make the page ask for less.
  • Throttling search engines on your public site. Google treats a 429 as a sign the server is overloaded and slows its crawling. If your marketing pages sit behind the same limiter and aren’t showing up, rule this out along with the usual causes in why a website is not ranking on Google.
  • No way to raise the limit. Your biggest customers will eventually need more. Limits per plan, or a simple way to ask for a higher one, turn throttling into an upgrade conversation instead of a support ticket.

Limits beyond the API

The same idea protects every public form you run. A contact form on a SaaS marketing page, or a quote request form on a trade site, gets hit by bots too. A per-IP limit on submissions, plus a hidden honeypot field, keeps most spam out without putting a puzzle in front of real people. The form still has to be quick for humans, which is a big part of why contractors’ websites don’t get the phone ringing.

How to roll it out safely

  1. Log first. Run the limiter in a log-only mode for a while, recording who would have been throttled.
  2. Review the log. If a legitimate customer would have been blocked, raise the ceiling or talk to them about their integration.
  3. Enforce with headers already in place, so clients can see their budget from the first request.
  4. Alert on 429s per customer. A sudden spike usually means a broken integration, and you want to know before they email you.

Done well, rate limiting is invisible to everyone except the traffic you actually wanted to stop. If you are building an API and want limits like this designed in from the start, it is part of how I approach SaaS development.

Questions people ask

What is API rate limiting?

API rate limiting caps how many requests a client can make in a period of time. It protects the service from abuse, runaway scripts and accidental overload, and it makes sure one heavy customer cannot slow the API down for everyone else. Good limits sit well above normal use, so real users never notice them.

What HTTP status code should a rate limited API return?

Return 429 Too Many Requests. Add a Retry-After header that says how long the client should wait, and ideally headers showing the limit, the requests remaining and when the window resets. Avoid 403 or 500, because clients cannot tell those apart from a permissions problem or a server failure.

Should I rate limit by IP address or by API key?

For authenticated APIs, limit by API key or account, so each customer has their own budget and an office sharing one IP is not punished. Use IP limits on endpoints with no account yet, such as login, signup and password reset, ideally combined with a limit on the email address being tried.

What is a token bucket rate limiter?

A token bucket gives each client a bucket that refills with tokens at a steady rate. Every request spends a token, and when the bucket is empty, requests are throttled. It allows short bursts up to the bucket size, which real apps produce, while still capping the sustained rate an abusive client can reach.

How do I choose the right rate limit for my API?

Start from real usage. Look at how many requests your heaviest legitimate customers send at peak, set the ceiling comfortably above that, and give expensive endpoints tighter limits than cheap ones. Run the limiter in log-only mode first so you can see who would have been blocked before you enforce anything.

Comments

Leave a comment

0 / 3000
Comments are moderated. Links are not clickable and your email is never published. See the privacy policy.
keep reading

Related articles

Usually replies within a day
let’s talk

Tell me what you want to build

A website, an app, some automation, or something you’re still figuring out. Tell me the problem and I’ll give you a straight answer on what it takes. No pressure, no sales team.