Back to resources
// technical · security

Rate limiting and API protection

Without limits, an API gets hammered: login brute-force, scraping, unintended abuse from a buggy client, or a cost spike on your LLM calls. Here's how to limit cleanly, without hurting real users.

TechnicalJul 27, 2026~7 min · intermediate
rate limitingRedistoken bucketHTTP 429Next.js
01

Why limit

01
Brute-force

On login, an attacker tries thousands of passwords. Limiting attempts breaks the attack.

02
Scraping & abuse

A bot scrapes your data, or a buggy client loops. The limit protects your resources.

03
LLM/API cost

An endpoint calling an LLM without a limit is an open bill. A per-user cap bounds it.

04
Fairness

One heavy consumer shouldn’t degrade everyone else’s service. The limit keeps sharing fair.

02

The token bucket

The simplest and most common algorithm: each key (IP or user) has a counter that fills over a time window. Beyond the limit, return a 429. Redis is perfect for it — atomic and shared across all your instances.

rate-limit.tscopy
// token bucket avec Redis : simple, distribue, rapide
import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
async function allow(key, limit, windowSec) {
const count = await redis.incr(key); // +1 sur la fenetre courante
if (count === 1) await redis.expire(key, windowSec);
return count <= limit; // false = on bloque
}
// dans une route : 10 requetes / 10s par IP
if (!(await allow(`rl:${ip}`, 10, 10))) {
return new Response("Too Many Requests", { status: 429 });
}
!

Limit per logged-in user when you can, not just per IP: behind a corporate NAT, hundreds of users share the same IP.

03

What to limit first

loginstrict: 5 attempts / minute / IP + account
signup / resetstrict: block mass account creation
endpoints coûteuxLLM, export, upload: per-user cap
API publiqueper-key quota, with X-RateLimit-* headers
04

Without breaking UX

return a clear 429 with a Retry-After header, not a silent error;
generous limits for normal use, tight for sensitive routes;
log the blocks: a spike of 429s reveals an attack or a client bug;
client-side, handle the 429 with backoff, not an aggressive retry loop.
i

Watching 429s is observability: I cover logs and alerts in monitoring: logs, alerts, uptime.

05

Sources & further reading

01MDN — 429 Too Many RequestsThe reference on the 429 status code and the Retry-After header that goes with it.02MDN — Retry-AfterExact header syntax: delay in seconds or an HTTP date.03Redis — INCR : pattern rate limiterThe INCR + EXPIRE pattern, including the race condition to fix with a Lua script.04Upstash Ratelimit — algorithmesFixed window, sliding window and token bucket compared, with the TypeScript SDK.05Cloudflare — How we built rate limiting capable of scaling to millions of domainsWhy sliding window: two counters per key, and it handles billions of requests.06OWASP API Security Top 10 — API4:2023 Unrestricted Resource ConsumptionMissing limits framed as a vulnerability, with the recommended countermeasures.07Vercel — WAF Rate LimitingLimiting at the edge before requests reach your code: keys, windows, 429 or challenge action.

Rate limiting is one of the best effort-to-payoff protections: a few lines with Redis, and you close the door to brute-force, scraping and cost overruns. Tight where it matters, generous elsewhere, and always return a polite 429.