← Back to @payez/next-mvp
Rate Limiting

Rate Limiting

Configurable per-route rate limits. Protection without configuration.

Every API route has rate limiting out of the box. Default limits protect against abuse immediately. Per-route overrides let you fine-tune for your specific use case. Redis-backed for distributed consistency.

How it works

1

The MVP applies a default rate limit to every API route: 100 requests per minute per user (authenticated) or per IP (unauthenticated). This kicks in immediately with no configuration.

2

Rate limit state is stored in Redis using a sliding window algorithm. This means limits work correctly across multiple app instances — if you have 3 servers, the user's 100 req/min limit is shared across all 3.

3

When a request exceeds the limit, the response is 429 Too Many Requests with Retry-After and X-RateLimit-* headers. The body includes a structured error with the limit, remaining requests, and reset time.

4

Per-route overrides let you set different limits for different endpoints — higher limits for read-heavy routes, lower limits for expensive operations like file uploads or payment processing.

Default protection

The most important thing about the rate limiting system is that it works without any configuration. Install the MVP, and every API route is rate-limited to 100 req/min per user. This protects against: accidental infinite loops in client code, credential stuffing attacks on login endpoints, API scraping, and runaway automation. You don't have to remember to add rate limiting — it's there.

Per-route configuration

Override the default limits for specific routes. Expensive operations get lower limits. Read-heavy endpoints get higher limits. Public endpoints (like health checks) can be excluded entirely.

// Rate limit configuration
// In your app config or environment

const rateLimits = {
  // Default: 100 req/min per user
  default: { window: '1m', max: 100 },

  // Override per route pattern
  routes: {
    // Login: stricter to prevent brute force
    '/api/auth/login': { window: '15m', max: 5 },

    // File upload: expensive operation
    '/api/uploads/*': { window: '1m', max: 10 },

    // Read-heavy: higher limit
    '/api/data/*': { window: '1m', max: 500 },

    // Webhooks from Stripe: no limit
    '/api/billing/webhook': { window: '1m', max: -1 },
  }
};

Response headers

Every API response includes rate limit headers so clients can implement backoff proactively — before they hit the limit. Well-behaved clients read these headers and pace their requests.

// Response headers on every API call
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1735686060

// When limit is exceeded
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735686060

{
  "error": "rate_limit_exceeded",
  "limit": 100,
  "remaining": 0,
  "retryAfter": 23,
  "window": "1m"
}

Sliding window algorithm

The rate limiter uses a Redis-backed sliding window — not a fixed window. This prevents the burst problem where a user could send 100 requests at 12:00:59 and 100 more at 12:01:01 (200 in 2 seconds) by straddling a fixed window boundary. The sliding window counts requests in a rolling 60-second window from the current time. No burst exploitation. Smooth, predictable limiting.

Keying strategies

Rate limits are keyed by: - Authenticated users: user ID from session (most common) - Unauthenticated requests: IP address - API key requests: API key identifier This means: if user A and user B both hit the same endpoint, they each get their own 100 req/min budget. One user's heavy usage doesn't affect another user.

Distributed consistency

Because rate limit state lives in Redis (not in-memory), it works identically across multiple app instances. If you're running 5 servers behind a load balancer, a user who sends 50 requests to server A and 50 to server B has used 100 of their 100 — not 50 on each. This is critical for production deployments. In-memory rate limiting breaks as soon as you scale horizontally.

This is included. Not extra.

Rate Limiting ships with every @payez/next-mvp install. 10K MAU free. No credit card required.