{"post":{"slug":"api-rate-limiting-2026","title":"API rate limiting in 2026: algorithms, patterns, and implementation","excerpt":"Rate limiting protects your API from abuse and keeps costs predictable, but the wrong algorithm causes thundering-herd failures. Here is the 2026 guide.","category":"code","date":"March 24, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">R</span>ate limiting is one of those API features teams add after their first incident, not before. A single runaway client, a retry storm, or a scraper can exhaust your database connections in seconds. The right rate-limiting architecture stops that while giving legitimate users headroom to burst — if you pick the right algorithm and store state correctly.\n\n## What changed in 2026\n\n- **Redis 8 shipped a native sliding-window rate-limit module** (`RL.THROTTLE`), making correct sliding-window counters trivial to deploy without Lua scripts.\n- **HTTP RFC 9110 companion drafts formalised `RateLimit` headers** — `RateLimit-Policy`, `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` — and major API gateways (Kong, Envoy, AWS API Gateway) now emit them by default.\n- **Edge rate limiting at CDN/WAF layer** (Cloudflare, Fastly) became the default for public-facing APIs, pushing the first line of defence to the network edge.\n- **AI API providers** established metered token-per-minute limits as the dominant quota unit, making token-bucket semantics the most widely understood model across engineering teams.\n\n## The four main algorithms\n\n| Algorithm | Burst handling | Reset spike | State size | Best for |\n| --- | --- | --- | --- | --- |\n| Fixed window counter | Allows 2× burst at boundary | Yes | Tiny | Internal metrics, coarse limits |\n| Sliding window log | Exact, no spikes | No | O(requests) | High-precision auditing |\n| Sliding window counter | Near-exact, no spikes | No | Tiny | Production default |\n| Token bucket | Smooth bursts up to bucket size | No | Tiny | User-facing APIs |\n| Leaky bucket | Forces constant output rate | No | Tiny | Egress shaping |\n\n**Token bucket** is the recommended default. The bucket holds up to `capacity` tokens; tokens refill at `rate` per second; each request consumes one (or N for weighted calls). Bursts are allowed up to the capacity ceiling, then the rate is enforced.\n\n## Token bucket: reference implementation\n\n```python\nimport time\nimport redis\n\ndef is_allowed(r: redis.Redis, key: str, capacity: int, rate: float) -> bool:\n    \"\"\"\n    Returns True if the request is allowed.\n    Uses a Redis hash to store (tokens, last_refill_time).\n    \"\"\"\n    now = time.time()\n    pipe = r.pipeline()\n    pipe.hgetall(key)\n    result, = pipe.execute()\n\n    tokens = float(result.get(b'tokens', capacity))\n    last = float(result.get(b'last', now))\n\n    # Refill tokens since last check\n    elapsed = now - last\n    tokens = min(capacity, tokens + elapsed * rate)\n\n    if tokens < 1:\n        return False  # Emit 429\n\n    tokens -= 1\n    r.hset(key, mapping={'tokens': tokens, 'last': now})\n    r.expire(key, int(capacity / rate) + 10)\n    return True\n```\n\nFor production, replace the non-atomic read-modify-write with a Lua script or use Redis 8's `RL.THROTTLE` to avoid race conditions under concurrent load.\n\n## Sliding window counter\n\nThe sliding window counter approximates a true sliding window using two adjacent fixed windows and a weighted interpolation — near-exact precision with O(1) state.\n\n```\ncurrent_count = window_count[current] +\n                window_count[previous] * (1 - elapsed_fraction_of_current_window)\n```\n\nIf `current_count >= limit`, reject. This is what Redis's `RL.THROTTLE` implements natively.\n\n## Correct HTTP headers\n\n```http\nHTTP/1.1 429 Too Many Requests\nContent-Type: application/json\nRateLimit-Policy: \"default\";l=100;w=60\nRateLimit-Limit: 100\nRateLimit-Remaining: 0\nRateLimit-Reset: 37\nRetry-After: 37\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1748908937\n\n{\"error\": \"rate_limit_exceeded\", \"retry_after_seconds\": 37}\n```\n\nAlways emit `Retry-After` (seconds). Emit both the draft RFC headers and the legacy `X-RateLimit-*` headers until client adoption of the new standard is complete.\n\n## How to pick your limits\n\n1. **Baseline your actual P99 usage per client tier** before setting limits — pulling numbers from thin air leads to limits that block legitimate users.\n2. **Set burst capacity at 2–5× the per-second rate** to absorb legitimate spikes (page load fans out to 5 API calls).\n3. **Differentiate tiers**: anonymous (strictest), authenticated (moderate), paying customer (generous), service-to-service (highest or unlimited).\n4. **Limit by IP for anonymous, by API key for authenticated** — IP-only limits break shared corporate NAT gateways.\n5. **Add a global limit per endpoint** separate from per-client limits to cap total load.\n\n## Common mistakes\n\n**In-process state with multiple pods.** If you run 10 app servers and track limits locally, each server sees 1/10 of actual traffic. Clients get 10× the intended limit. Use Redis (or Memcached) for shared state.\n\n**Not handling Redis failure gracefully.** If Redis is unavailable, don't crash or block all requests — fail open with a circuit breaker, log the bypass, and alert.\n\n**Silently dropping requests.** Always return 429, never silently discard. Clients need the signal to back off.\n\n**Counting retries against the limit without Retry-After.** A client that retries immediately on 429 can generate more load than the original request. The `Retry-After` header is the only reliable mechanism to break that loop.\n\n**Single global counter across all endpoints.** A bulk export endpoint burning the same quota as lightweight reads is unfair. Weight heavy endpoints or give them separate buckets.\n\n## What to skip\n\n- **Rolling your own distributed counter in a relational database** — the write throughput required at API scale will saturate your DB. Redis is purpose-built for this.\n- **Fixed-window counters for end-user quotas** — the burst-at-boundary problem is real and users will notice the cliff.\n- **Blocking at the application layer for DDoS** — CDN/WAF edge rate limiting should absorb floods before packets reach your app servers.\n\n## FAQ\n\n**Should I rate limit by IP or by API key?**\nBoth, at different layers. IP limits at the edge/WAF for anonymous traffic; API key limits at the API gateway for authenticated traffic. IP-only limits break behind NATs.\n\n**What bucket size and refill rate should I start with?**\nStart with a burst size of 60 tokens and refill of 10/second for a typical authenticated API endpoint — 600 requests/minute sustained with bursts up to 60 in a second. Tune from real traffic data.\n\n**How do I test rate limiting in CI?**\nUse a fake Redis (Fakeredis in Python, `ioredis-mock` in Node) and write parameterized tests that send N+1 requests and assert the (N+1)th returns 429 with correct headers.\n\n**Can I use API Gateway (AWS/GCP) instead of building my own?**\nYes, and you should for the outer layer. AWS API Gateway, Google Cloud Endpoints, and Kong all provide built-in rate limiting. Roll your own only for business-logic-aware limits (e.g., per-resource quotas) the gateway can't express.\n\n## Where to go next\n\nSee [Caching strategies in 2026](/blog/caching-strategies-2026) for reducing load so your rate limits are rarely hit, and [Feature flags guide in 2026](/blog/feature-flags-guide-2026) for gradually rolling out limit changes without a full redeployment.","hue":306,"metaTitle":"API Rate Limiting 2026: Algorithms and Redis","metaDescription":"Complete guide to API rate limiting in 2026. Token bucket vs sliding window, Redis implementation, correct HTTP headers, and per-tier quota design.","keywords":["api rate limiting 2026","token bucket algorithm","sliding window rate limit","rate limiting redis","429 too many requests","rate limit headers http","api quota design","leaky bucket rate limiting","rate limiting middleware","api throttling 2026"],"tldr":["**Token bucket is the go-to algorithm** — it smooths bursts while enforcing an average rate, and maps naturally to most API quota models.","**Store rate-limit state in Redis, not in-process** — horizontal scaling requires shared state; in-process counters let users bypass limits by hitting different pods.","**Return 429 with Retry-After and X-RateLimit-* headers** — clients that see structured metadata can back off intelligently instead of hammering retry loops.","**Differentiate by identity tier** — anonymous < authenticated < paid < internal; flat limits treat your best customers like scrapers.","**Skip:** fixed-window counters for user-facing APIs — they create predictable reset spikes at the window boundary."],"readTime":"5 min read","wordCount":1064},"related":[{"slug":"api-rate-limiting-vs-throttling-2026","title":"API Rate Limiting vs Throttling in 2026: The Real Difference","excerpt":"Rate limiting and throttling both control API traffic, but they enforce different contracts. Here is how each works, when to use which, and how to design limits that do not break legitimate clients.","category":"code","date":"July 5, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":290},{"slug":"how-to-rate-limit-an-api-2026","title":"How to rate limit an API in 2026","excerpt":"Rate limiting protects your API from abuse and runaway clients. This guide covers token bucket, sliding window, and Redis-backed strategies for 2026.","category":"code","date":"April 2, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":311},{"slug":"rate-limiters-explained-2026","title":"Rate limiters explained in 2026: algorithms, tradeoffs, and implementation","excerpt":"Rate limiting protects your API from abuse and overload, but choosing the wrong algorithm causes user pain. Here is the practical 2026 guide.","category":"code","date":"March 19, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":306}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"airpods-vs-galaxy-buds-2026","title":"AirPods vs Galaxy Buds in 2026: which wireless earbuds win?","excerpt":"AirPods and Galaxy Buds are both excellent in 2026, but each earns its edge on different axes. Here is the honest comparison for iPhone and Android users alike.","category":"tech","date":"March 24, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":234},"older":{"slug":"authorization-explained-2026","title":"Authorization explained in 2026: RBAC, ABAC, and ReBAC compared","excerpt":"Authorization determines what authenticated users can actually do. This is the 2026 guide to RBAC, ABAC, and relationship-based access control.","category":"code","date":"March 24, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":313}}