Skip to main content

How to Add Rate Limiting to an API

Protect an API with token-bucket rate limiting keyed per client, backed by Redis so limits hold across instances. Return standard RateLimit headers and 429 with Retry-After when exceeded.

Difficulty
Intermediate
Duration
40 minutes
Steps
5

What and why

Rate limiting caps how many requests a client may make in a window, protecting an API from abuse, accidental floods, and runaway costs. It also enforces fair use across tenants. Because most APIs run multiple instances, limits should be shared, typically in Redis.

Prerequisites

  • A running API.
  • Redis available for shared counters.
  • Basic HTTP knowledge.

Steps

1. Choose a limiting algorithm

Token bucket allows short bursts while capping the long-term rate, and is the most common choice. Sliding window gives smoother enforcement. Fixed window is simplest but allows double bursts at window edges.

2. Identify the client key

Limit per API key for authenticated clients, falling back to client IP for anonymous traffic. Be careful behind proxies: trust the correct forwarded header, or attackers spoof it.

3. Enforce limits with Redis

Use an atomic operation so concurrent requests count correctly. With express-rate-limit and a Redis store:

const rateLimit = require('express-rate-limit');
const { RedisStore } = require('rate-limit-redis');
app.use(rateLimit({
  windowMs: 60_000,
  limit: 100,
  standardHeaders: 'draft-7',
  store: new RedisStore({ sendCommand: (...a) => redis.call(...a) }),
  keyGenerator: (req) => req.apiKey || req.ip,
}));

A shared store means the limit holds across all instances.

4. Return rate-limit headers

Return the standard headers so clients can self-throttle:

RateLimit-Limit: 100
RateLimit-Remaining: 12
RateLimit-Reset: 30

When the limit is exceeded, respond 429 Too Many Requests with a Retry-After header.

5. Handle bursts and exemptions

Allow a small burst above the steady rate for legitimate spikes, and exempt health checks and internal traffic. Apply tiered limits by plan (free vs paid) using different keys.

Verification

  • Exceeding the limit returns 429 with Retry-After.
  • Remaining-count headers decrement across requests.
  • Limits hold when requests hit different instances.

Next Steps

Move limiting to the edge (an API gateway or Nginx limit_req) to shed load before it reaches your app, add per-endpoint limits for expensive routes, and emit metrics so you can tune thresholds from real traffic.

Prerequisites

  • A running API
  • Redis available
  • Basic HTTP knowledge

Steps

  • 1
    Choose a limiting algorithm
  • 2
    Identify the client key
  • 3
    Enforce limits with Redis
  • 4
    Return rate-limit headers
  • 5
    Handle bursts and exemptions

Category

API Design