0
0
Rest-apiHow-ToBeginner · 4 min read

How to Implement Rate Limiting in REST APIs

To implement rate limiting in a REST API, you track the number of requests a client makes within a time window and block or delay requests that exceed the limit. This can be done using in-memory counters, external stores like Redis, or middleware that checks and enforces limits per client IP or API key.
📐

Syntax

Rate limiting typically involves these parts:

  • Identifier: A way to identify the client, such as IP address or API key.
  • Limit: Maximum allowed requests in a time window.
  • Window: Time period for counting requests (e.g., 1 minute).
  • Counter: Tracks how many requests the client has made in the current window.
  • Action: What to do when the limit is exceeded (block, delay, or respond with error).
javascript
function rateLimit(clientId) {
  const limit = 100; // max requests
  const windowMs = 60000; // 1 minute
  const requestCounts = new Map();

  const now = Date.now();
  if (!requestCounts.has(clientId)) {
    requestCounts.set(clientId, { count: 1, startTime: now });
    return true; // allow
  }

  const data = requestCounts.get(clientId);
  if (now - data.startTime < windowMs) {
    if (data.count < limit) {
      data.count++;
      return true; // allow
    } else {
      return false; // limit exceeded
    }
  } else {
    requestCounts.set(clientId, { count: 1, startTime: now });
    return true; // new window
  }
}
💻

Example

This example shows a simple Express.js REST API with rate limiting middleware that allows 5 requests per minute per client IP.

javascript
import express from 'express';

const app = express();
const port = 3000;

const rateLimitWindowMs = 60000; // 1 minute
const maxRequests = 5;
const clients = new Map();

function rateLimiter(req, res, next) {
  const clientIp = req.ip;
  const now = Date.now();

  if (!clients.has(clientIp)) {
    clients.set(clientIp, { count: 1, startTime: now });
    return next();
  }

  const clientData = clients.get(clientIp);

  if (now - clientData.startTime < rateLimitWindowMs) {
    if (clientData.count < maxRequests) {
      clientData.count++;
      next();
    } else {
      res.status(429).send('Too many requests - try again later');
    }
  } else {
    clients.set(clientIp, { count: 1, startTime: now });
    next();
  }
}

app.use(rateLimiter);

app.get('/', (req, res) => {
  res.send('Hello, world!');
});

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});
Output
Server running on http://localhost:3000 // On 6th request within 1 minute from same IP: // HTTP 429 Too many requests - try again later
⚠️

Common Pitfalls

  • Using only in-memory counters: This fails in distributed systems or when the server restarts, losing counts.
  • Not identifying clients properly: Using IP alone may block many users behind the same proxy.
  • Too strict limits: Can block legitimate users and harm user experience.
  • Not sending proper HTTP status: Use 429 Too Many Requests to inform clients.
javascript
/* Wrong: No client identification, global limit */
let count = 0;
const limit = 5;

function badRateLimit(req, res, next) {
  if (count < limit) {
    count++;
    next();
  } else {
    res.status(429).send('Too many requests');
  }
}

/* Right: Identify client by IP and reset counts per window */
const clients = new Map();
const windowMs = 60000;

function goodRateLimit(req, res, next) {
  const ip = req.ip;
  const now = Date.now();
  if (!clients.has(ip) || now - clients.get(ip).startTime > windowMs) {
    clients.set(ip, { count: 1, startTime: now });
    next();
  } else if (clients.get(ip).count < limit) {
    clients.get(ip).count++;
    next();
  } else {
    res.status(429).send('Too many requests');
  }
}
📊

Quick Reference

  • Identify clients uniquely (IP, API key, user ID).
  • Set a reasonable request limit and time window.
  • Use persistent or distributed storage for counters in multi-server setups.
  • Respond with HTTP 429 status when limit is exceeded.
  • Consider using existing libraries or API gateways for robust rate limiting.

Key Takeaways

Track requests per client within a fixed time window to enforce limits.
Use unique client identifiers like IP or API keys for accurate counting.
Respond with HTTP 429 status code when the limit is exceeded.
Avoid in-memory counters alone in distributed or production environments.
Leverage existing middleware or services for scalable rate limiting.