Skip to main content

Rate Limiting (Request Frequency Limitation and API Protection)

A systemic mechanism for controlling the intensity of incoming and outgoing traffic (Token Bucket, Sliding Window) to protect the backend from resource exhaustion, brute force attacks, Layer 7 DDoS, and financial overdraft on AI endpoints.

1. Concept Overview & Systemic Problem

Without a reliable rate limiting mechanism, any open web service or API endpoint is under constant threat of technical or financial collapse:

  • Financial Drain (AI Billing Drain): An attacker or looping script can send 100,000 requests to your code or image generation route in 5 minutes, generating a bill of $5,000 at OpenAI or Anthropic.
  • Brute Force and Credential Stuffing: Automated botnets iterate through millions of passwords on the /api/auth/login form, overwhelming the database with heavy bcrypt/Argon2 hashing operations.
  • Cascading Database Failure (Denial of Service): Mass parallel calls to uncached search endpoints exhaust the PostgreSQL connection pool, paralyzing the entire application.

Rate Limiting is a fundamental shield of system security. It measures traffic intensity by identifier (IP address, session ID, API key) and cuts off excess traffic early in the pipeline, ensuring predictable load and continuity of service for legitimate users.

2. Architectural Taxonomy & Mental Model

Architectural choices for traffic limiting algorithms and implementation levels:

┌─────────────────────────────────────────────────────────────┐
│                 RATE LIMITING ALGORITHMIC TAXONOMY          │
├─────────────────────────────────────────────────────────────┤
│ 1. Fixed Window: Simple counter resetting every minute       │
│    (Vulnerable to 2x traffic bursts at interval boundaries)   │
├─────────────────────────────────────────────────────────────┤
│ 2. Sliding Window Counter: Hybrid sliding window              │
│    Weight = Prev_Count * (1 - Elapsed_Ratio) + Curr_Count   │
│    (Ideal compromise: accuracy + minimal RAM consumption)    │
├─────────────────────────────────────────────────────────────┤
│ 3. Token Bucket: Token replenishment at a constant rate      │
│    (Allows legitimate traffic bursts up to bucket capacity)   │
├─────────────────────────────────────────────────────────────┤
│ 4. Leaky Bucket: Constant outflow rate from the queue        │
│    (Optimal for smoothing outgoing requests to APIs)         │
└─────────────────────────────────────────────────────────────┘
  1. Hybrid Sliding Window (Sliding Window Counter):
    • Maintains counters for the current and previous time intervals. For each request, it calculates a weighted sum based on how much time has elapsed in the current window, excluding anomalies at minute boundaries.
  2. Client Identifier Hierarchy (Subject Identification):
    • Public Level: IP address (for anonymous requests and DDoS protection).
    • Authenticated Level: User_ID or Organization_ID (ignores shared corporate NAT/VPN networks).
    • Token Level: Granular quotas based on the purchased plan type (Free, Pro, Enterprise).
  3. Distributed State Storage Backend (Redis Cluster / Upstash):
    • A centralized in-memory storage that synchronizes counters across dozens of backend instances in microseconds.

3. Technical Pipeline & Internal Mechanics

Request verification lifecycle through Redis with an atomic Lua script:

  1. Extracting Identifier and Route: Middleware intercepts the incoming request. A combined key is formed: rate:auth:${req.ip} for login forms or rate:llm:${user.id} for AI routes.
  2. Atomic Execution of Lua Script in Redis: To avoid race conditions between parallel requests, all logic is packed into a single Lua script:
    • Current timestamp is read.
    • Records older than the sliding window size (60 seconds) are removed.
    • The number of active tokens is calculated.
  3. Decision Making:
    • If the limit is not exceeded: the counter is incremented by 1 (or by the token cost of the request), a TTL is set for the key, and the request is passed to the controller.
    • If the limit is exceeded: a denial response is generated.
  4. Standard HTTP Header Formation: The server returns metadata to the client:
    HTTP/1.1 429 Too Many Requests
    Content-Type: application/json
    Retry-After: 24
    X-RateLimit-Limit: 10
    X-RateLimit-Remaining: 0
    X-RateLimit-Reset: 1718293840
    
  5. Telemetry and Alerting: The limit exceedance event is logged in the monitoring system; if an abnormal spike from a single subnet occurs, a blocking rule is triggered in the firewall.

4. Production Engineering Scenarios

01. Protecting Expensive Generative AI Routes (Token-Aware Limiting)

The code generation route deducts tokens proportionally to the complexity of the request:

  • Instead of simply counting requests, the rate limiter deducts from the user's virtual balance the exact number of generated tokens.
  • Free-tier users have a limit of 50,000 tokens per day. Once the limit is exhausted, the API blocks generation until the next day.

02. Protecting Authentication Forms from Brute Force (Credential Defense)

The endpoint /api/auth/sign-in:

  • No more than 5 failed password attempts are allowed within 15 minutes for a single combination of IP + Email.
  • On the 6th attempt, the system requires CAPTCHA completion or sends a password reset link to the email, completely nullifying dictionary attack attempts.

03. Smoothing Outgoing Traffic to Third-Party Payment Gateways

Integration with Stripe, where a limit of 100 requests per second applies:

  • An internal microservice uses a queue with the Leaky Bucket algorithm.
  • Even if internal company agents simultaneously create 500 payments during Black Friday, the queue sends requests at a strictly uniform rate (80 req/s), eliminating the risk of Stripe account blocking.

5. Pitfalls, Common Mistakes & Security

  • Blocking Entire Offices Due to Naive IP Rate Limiting: Applying strict IP limits to authorized users can block all colleagues sharing a corporate NAT address due to one active employee. Always limit by user_id after login.
  • Race Condition with Separate GET and INCR Operations: If the limit is checked with a GET command and then increased with INCR, an attacker can send 100 simultaneous asynchronous requests, all passing the check before the counter updates. Use atomic scripts.
  • Redis Memory Overflow from Storing Timestamps: Using unordered lists to store timestamps for each request without a strict TTL can quickly consume gigabytes of RAM during a mass attack.
  • Ignoring Proxy Headers (X-Forwarded-For Spoofing): If your server is behind Cloudflare or Nginx and reads the IP directly from the socket, you will receive the proxy's local address. Conversely, blind trust in the unverified X-Forwarded-For header allows an attacker to spoof any IP address.
/ Frequently Asked QuestionsSchema.org FAQPage

FAQ: Rate Limiting (Request Frequency Limitation and API Protection)

Sliding Window Counter (hybrid sliding window) or Token Bucket. They smooth out short-term legitimate traffic bursts while reliably preventing limit doubling at the boundaries of time intervals, a common issue with naive Fixed Window.
/ Internal links
All terms