{"post":{"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","content":"<span class=\"dropcap\">R</span>ate limiting is the difference between an API that stays up under abuse and one that falls over. In 2026, the common pattern is a Redis-backed sliding window for distributed deployments, edge-level blocking for known abusers, and standardized response headers so clients can self-throttle. The good news: the libraries are mature enough that you can add solid rate limiting in an afternoon.\n\n## What changed in 2026\n\n- **Upstash Rate Limit** became the go-to library for serverless/edge deployments — HTTP-based Redis, no persistent connections, works on Cloudflare Workers.\n- **IETF Rate Limit Headers draft** was finalized; standardized headers mean clients can handle 429s programmatically.\n- **Edge providers baked in rate limiting** — Cloudflare Rate Limiting, Vercel Firewall, and AWS WAF Managed Rules can enforce limits before your code runs.\n- **Durable Objects (Cloudflare)** enable stateful per-user rate limiting at the edge without a separate Redis cluster.\n\n## Algorithms compared\n\n| Algorithm | Accuracy | Burst handling | Implementation complexity |\n|---|---|---|---|\n| Fixed window | Low (boundary spikes) | Poor | Trivial |\n| Sliding window (log) | High | Good | Medium |\n| Sliding window (counter) | Good | Good | Low |\n| Token bucket | High | Excellent (controlled burst) | Medium |\n| Leaky bucket | High | None (smooths traffic) | Medium |\n\n**Sliding window counter** is the sweet spot for most APIs: accurate enough, Redis-efficient (two operations per request), and easy to reason about.\n\n## Express + `express-rate-limit` + Redis store\n\n```bash\nnpm install express-rate-limit @express-rate-limit/redis rate-limiter-flexible ioredis\n```\n\n```js\nimport rateLimit from 'express-rate-limit';\nimport { RedisStore } from '@express-rate-limit/redis';\nimport { createClient } from 'redis';\n\nconst redis = createClient({ url: process.env.REDIS_URL });\nawait redis.connect();\n\nconst limiter = rateLimit({\n  windowMs: 60_000, // 1 minute\n  max: 100,         // 100 requests per window\n  standardHeaders: 'draft-7', // RateLimit-* headers\n  legacyHeaders: false,\n  store: new RedisStore({ sendCommand: (...args) => redis.sendCommand(args) }),\n  keyGenerator: (req) => req.headers['x-api-key'] ?? req.ip,\n});\n\napp.use('/api/', limiter);\n```\n\n`keyGenerator` lets you rate-limit by API key (for authenticated routes) or IP (for public endpoints).\n\n## Upstash for serverless / edge\n\n```bash\nnpm install @upstash/ratelimit @upstash/redis\n```\n\n```ts\nimport { Ratelimit } from '@upstash/ratelimit';\nimport { Redis } from '@upstash/redis';\n\nconst ratelimit = new Ratelimit({\n  redis: Redis.fromEnv(),\n  limiter: Ratelimit.slidingWindow(100, '1 m'),\n  analytics: true,\n});\n\n// In your edge function / API route:\nconst identifier = request.headers.get('x-api-key') ?? 'anonymous';\nconst { success, limit, remaining, reset } = await ratelimit.limit(identifier);\n\nif (!success) {\n  return new Response('Too Many Requests', {\n    status: 429,\n    headers: {\n      'RateLimit-Limit': String(limit),\n      'RateLimit-Remaining': String(remaining),\n      'RateLimit-Reset': String(reset),\n      'Retry-After': String(Math.ceil((reset - Date.now()) / 1000)),\n    },\n  });\n}\n```\n\nThis works on Cloudflare Workers, Vercel Edge, and Next.js Middleware with zero modifications.\n\n## Tiered limits by API key\n\n```js\nconst plans = { free: 60, pro: 600, enterprise: 6000 };\n\nconst keyGenerator = async (req) => {\n  const key = req.headers['x-api-key'];\n  const plan = await getPlanForKey(key); // DB lookup, cached\n  return `${plan}:${key}`;\n};\n\nconst limiter = rateLimit({\n  max: async (req) => {\n    const key = req.headers['x-api-key'];\n    const plan = await getPlanForKey(key);\n    return plans[plan] ?? plans.free;\n  },\n  // ...\n});\n```\n\nCache the plan lookup in Redis (TTL ~5 min) to avoid hitting your DB on every request.\n\n## How to pick a strategy\n\n1. **Single server, low traffic** → in-memory `express-rate-limit` with no store is fine.\n2. **Multi-instance or microservices** → Redis-backed sliding window; any instance sees the same counters.\n3. **Serverless / edge** → Upstash + sliding window; HTTP Redis, no persistent connection needed.\n4. **Need burst allowance** → token bucket (`rate-limiter-flexible` supports it).\n5. **Protecting login / sensitive endpoints** → stricter limits (5–10 req/min) keyed by IP + username.\n\n## Common mistakes\n\n**Same limit for all endpoints.** Your `/login` endpoint should be ~5 req/min per IP; your public search might be 1000 req/min. Differentiate by route.\n\n**Not returning 429 with headers.** A bare 429 with no `Retry-After` header causes clients to retry immediately, compounding the load. Always include the header.\n\n**Rate-limiting by IP behind a load balancer.** If `X-Forwarded-For` is not trusted and parsed correctly, every user appears to be the load balancer IP. Configure `app.set('trust proxy', 1)` in Express.\n\n**No rate-limiting on auth endpoints.** Login, registration, and password-reset endpoints are prime targets for credential stuffing. These need aggressive limits and lockout logic.\n\n## What to skip\n\n- **IP blocklists as your primary defense** — IPs rotate; blocklists create more maintenance than protection. Use rate limits as the primary mechanism.\n- **Very short windows (< 1 s)** for most endpoints — they create a noisy experience for legitimate power users. 1-minute windows work for most cases.\n- **Client-side rate limiting** — never trust it; enforce limits server-side always.\n\n## FAQ\n\n**What HTTP status code should a rate-limited response return?**\n`429 Too Many Requests`. Include `Retry-After` (seconds until the limit resets) and `RateLimit-Remaining: 0` headers.\n\n**How do I rate-limit authenticated users differently from anonymous ones?**\nUse the API key or user ID as the `keyGenerator` for authenticated requests, and IP for anonymous ones. Apply different `max` values per tier.\n\n**Should I rate-limit at the application or infra level?**\nBoth, in layers. Edge/WAF rules catch obvious abuse before your app; application-level rules enforce per-user semantics.\n\n**How do I handle distributed denial-of-service (DDoS)?**\nRate limiting alone is not DDoS protection. Use a CDN/WAF (Cloudflare, AWS Shield) for volumetric attacks; rate limiting handles application-layer abuse.\n\n## Where to go next\n\n- [How to set up Redis caching in 2026](/blog/how-to-set-up-redis-caching-2026)\n- [How to add authentication to an app in 2026](/blog/how-to-add-authentication-to-an-app-2026)\n- [How to build a REST API in Node in 2026](/blog/how-to-build-a-rest-api-in-node-2026)","hue":311,"metaTitle":"How to Rate Limit an API in 2026 (Redis + Edge)","metaDescription":"Rate limit your API in 2026 with sliding window counters, Redis, upstash/ratelimit, and edge-level rate limiting.","keywords":["how to rate limit an api 2026","api rate limiting 2026","redis rate limiting","sliding window rate limit","token bucket algorithm","upstash rate limit","express rate limit middleware","api abuse prevention","rate limit headers","serverless rate limiting 2026"],"tldr":["**Use a sliding window counter backed by Redis** — it is the most accurate and widely deployed algorithm for distributed APIs at minimal complexity cost.","**Return standard headers** — `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `Retry-After` on 429s; clients can back off intelligently.","**Rate-limit at the edge when possible** — Cloudflare, AWS WAF, and Vercel all offer rate limiting before traffic hits your servers.","**Use `upstash/ratelimit` for serverless** — a Redis-backed library designed for edge and serverless runtimes; zero cold-start overhead.","**Skip:** in-memory rate limiting on multi-instance deployments — counters per-process means each instance sees only its own traffic share and undercount abuse."],"readTime":"4 min read","wordCount":920},"related":[{"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","readTime":"5 min read","hue":306},{"slug":"rate-limit-algorithms-2026","title":"Rate Limit Algorithms: Picking the Right One","excerpt":"Fixed windows allow double bursts at the boundary; token buckets allow bursts by design. Here is how each algorithm behaves and which to use where.","category":"code","date":"July 24, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":132},{"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}],"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":"how-to-sync-files-across-devices-2026","title":"How to sync files across devices in 2026: cloud, local, and hybrid options","excerpt":"Keeping files in sync across phones, laptops, and desktops no longer requires one service to rule them all. Here are the best methods for every situation in 2026.","category":"tech","date":"April 3, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":236},"older":{"slug":"how-to-read-a-1099-2026","title":"How to read a 1099 in 2026: every box explained simply","excerpt":"A 1099 form can look intimidating but most people only need a handful of boxes. Here is a plain-English guide to reading any 1099 in 2026.","category":"finance","date":"April 2, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":278}}