
Api Rate Limiting
- 579 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
api-rate-limiting is a backend skill that teaches token bucket, sliding window, and fixed window rate limiting patterns for developers who need to protect APIs from abuse and manage traffic spikes.
About
api-rate-limiting is a Claude Code skill from aj-geddes/useful-ai-prompts that walks developers through implementing API rate limiting with three core algorithms: token bucket, sliding window, and fixed window. The skill covers per-user, per-IP, and per-endpoint strategies plus quick-start setup, reference guides, and best practices for tiered limits. Developers reach for api-rate-limiting when hardening REST or GraphQL endpoints against brute-force attacks, smoothing traffic spikes, or enforcing plan-based quotas before launch. The guidance is algorithm-focused rather than framework-specific, so teams can map patterns onto Express, FastAPI, NestJS, or gateway middleware. Use it during backend build when abuse protection is required but a full API gateway product is not yet in place.
- Implements token bucket, sliding window, and fixed window algorithms
- Supports per-user, per-IP, and per-endpoint rate limiting strategies
- Protects against brute force attacks, traffic spikes, and DoS attempts
- Enables tiered service plans and quota enforcement
- Includes JavaScript token bucket class with refill and consume methods
Api Rate Limiting by the numbers
- 579 all-time installs (skills.sh)
- Ranked #710 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill api-rate-limitingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 579 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you implement API rate limiting algorithms?
Add robust rate limiting to their APIs using token bucket, sliding window, or fixed window algorithms.
Who is it for?
Backend developers adding abuse protection and tiered quotas to HTTP APIs during initial backend build.
Skip if: Teams that already operate a managed API gateway with built-in rate limiting and only need dashboard tuning.
When should I use this skill?
A developer asks to add rate limiting, throttle API traffic, or implement token bucket or sliding window limits.
What you get
Rate-limiting middleware design, algorithm choice notes, and per-scope limit configuration for production APIs.
- Rate-limit middleware design
- Per-scope limit configuration
By the numbers
- Documents 3 rate-limiting algorithms: token bucket, sliding window, and fixed window
- Covers 3 limit scopes: per-user, per-IP, and per-endpoint
Files
API Rate Limiting
Table of Contents
Overview
Protect APIs from abuse and manage traffic using various rate limiting algorithms with per-user, per-IP, and per-endpoint strategies.
When to Use
- Protecting APIs from brute force attacks
- Managing traffic spikes
- Implementing tiered service plans
- Preventing DoS attacks
- Fairness in resource allocation
- Enforcing quotas and usage limits
Quick Start
Minimal working example:
// Token Bucket Rate Limiter
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate; // tokens per second
this.lastRefillTime = Date.now();
}
refill() {
const now = Date.now();
const timePassed = (now - this.lastRefillTime) / 1000;
const tokensToAdd = timePassed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefillTime = now;
}
consume(tokens = 1) {
this.refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return true;
}
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Token Bucket Algorithm | Token Bucket Algorithm |
| Sliding Window Algorithm | Sliding Window Algorithm |
| Redis-Based Rate Limiting | Redis-Based Rate Limiting |
| Tiered Rate Limiting | Tiered Rate Limiting |
| Python Rate Limiting (Flask) | Python Rate Limiting (Flask) |
| Response Headers | Response Headers |
Best Practices
✅ DO
- Include rate limit headers in responses
- Use Redis for distributed rate limiting
- Implement tiered limits for different user plans
- Set appropriate window sizes and limits
- Monitor rate limit metrics
- Provide clear retry guidance
- Document rate limits in API docs
- Test under high load
❌ DON'T
- Use in-memory storage in production
- Set limits too restrictively
- Forget to include Retry-After header
- Ignore distributed scenarios
- Make rate limits public (security)
- Use simple counters for distributed systems
- Forget cleanup of old data
Python Rate Limiting (Flask)
Python Rate Limiting (Flask)
from flask import Flask, request, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from datetime import datetime, timedelta
import redis
app = Flask(__name__)
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
# Custom rate limit based on user plan
redis_client = redis.Redis(host='localhost', port=6379)
def get_rate_limit(user_id):
plan = redis_client.get(f'user:{user_id}:plan').decode()
limits = {
'free': (100, 3600),
'pro': (10000, 3600),
'enterprise': (None, None)
}
return limits.get(plan, (100, 3600))
@app.route('/api/data', methods=['GET'])
@limiter.limit("30 per minute")
def get_data():
return jsonify({'data': 'api response'}), 200
@app.route('/api/premium', methods=['GET'])
def get_premium_data():
user_id = request.user_id
max_requests, window = get_rate_limit(user_id)
if max_requests is None:
return jsonify({'data': 'unlimited data'}), 200
key = f'ratelimit:{user_id}'
current = redis_client.incr(key)
redis_client.expire(key, window)
if current <= max_requests:
return jsonify({'data': 'premium data'}), 200
else:
return jsonify({'error': 'Rate limit exceeded'}), 429Redis-Based Rate Limiting
Redis-Based Rate Limiting
const redis = require("redis");
const client = redis.createClient();
// Sliding window with Redis
const redisRateLimit = (maxRequests, windowSeconds) => {
return async (req, res, next) => {
const key = `ratelimit:${req.user?.id || req.ip}`;
const now = Date.now();
const windowStart = now - windowSeconds * 1000;
try {
// Remove old requests
await client.zremrangebyscore(key, 0, windowStart);
// Count requests in window
const count = await client.zcard(key);
if (count < maxRequests) {
// Add current request
await client.zadd(key, now, `${now}-${Math.random()}`);
// Set expiration
await client.expire(key, windowSeconds);
res.setHeader("X-RateLimit-Limit", maxRequests);
res.setHeader("X-RateLimit-Remaining", maxRequests - count - 1);
next();
} else {
const oldestRequest = await client.zrange(key, 0, 0);
const resetTime = parseInt(oldestRequest[0]) + windowSeconds * 1000;
const retryAfter = Math.ceil((resetTime - now) / 1000);
res.set("Retry-After", retryAfter);
res.status(429).json({
error: "Rate limit exceeded",
retryAfter,
});
}
} catch (error) {
console.error("Rate limit error:", error);
next(); // Allow request if Redis fails
}
};
};
app.get("/api/expensive", redisRateLimit(10, 60), (req, res) => {
res.json({ result: "expensive operation" });
});Response Headers
Response Headers
// Standard rate limit headers
res.setHeader('X-RateLimit-Limit', maxRequests); // Total requests allowed
res.setHeader('X-RateLimit-Remaining', remaining); // Remaining requests
res.setHeader('X-RateLimit-Reset', resetTime); // Unix timestamp of reset
res.setHeader('Retry-After', secondsToWait); // How long to wait
// 429 Too Many Requests response
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 60,
"resetAt": "2025-01-15T15:00:00Z"
}Sliding Window Algorithm
Sliding Window Algorithm
class SlidingWindowLimiter {
constructor(maxRequests, windowSizeSeconds) {
this.maxRequests = maxRequests;
this.windowSize = windowSizeSeconds * 1000; // Convert to ms
this.requests = [];
}
isAllowed() {
const now = Date.now();
const windowStart = now - this.windowSize;
// Remove old requests outside window
this.requests = this.requests.filter((time) => time > windowStart);
if (this.requests.length < this.maxRequests) {
this.requests.push(now);
return true;
}
return false;
}
remaining() {
const now = Date.now();
const windowStart = now - this.windowSize;
this.requests = this.requests.filter((time) => time > windowStart);
return Math.max(0, this.maxRequests - this.requests.length);
}
}
const slidingWindowRateLimit = (maxRequests, windowSeconds) => {
const limiters = new Map();
return (req, res, next) => {
const key = req.user?.id || req.ip;
if (!limiters.has(key)) {
limiters.set(key, new SlidingWindowLimiter(maxRequests, windowSeconds));
}
const limiter = limiters.get(key);
if (limiter.isAllowed()) {
res.setHeader("X-RateLimit-Limit", maxRequests);
res.setHeader("X-RateLimit-Remaining", limiter.remaining());
next();
} else {
res.status(429).json({ error: "Rate limit exceeded" });
}
};
};
app.get("/api/search", slidingWindowRateLimit(30, 60), (req, res) => {
res.json({ results: [] });
});Tiered Rate Limiting
Tiered Rate Limiting
const RATE_LIMITS = {
free: { requests: 100, window: 3600 }, // 100 per hour
pro: { requests: 10000, window: 3600 }, // 10,000 per hour
enterprise: { requests: null, window: null }, // Unlimited
};
const tieredRateLimit = async (req, res, next) => {
const user = req.user;
const plan = user?.plan || "free";
const limits = RATE_LIMITS[plan];
if (!limits.requests) {
return next(); // Unlimited plan
}
const key = `ratelimit:${user.id}`;
const now = Date.now();
const windowStart = now - limits.window * 1000;
try {
await client.zremrangebyscore(key, 0, windowStart);
const count = await client.zcard(key);
if (count < limits.requests) {
await client.zadd(key, now, `${now}-${Math.random()}`);
await client.expire(key, limits.window);
res.setHeader("X-RateLimit-Limit", limits.requests);
res.setHeader("X-RateLimit-Remaining", limits.requests - count - 1);
res.setHeader("X-Plan", plan);
next();
} else {
res.status(429).json({
error: "Rate limit exceeded",
plan,
upgradeUrl: "/plans",
});
}
} catch (error) {
next();
}
};
app.use(tieredRateLimit);Token Bucket Algorithm
Token Bucket Algorithm
// Token Bucket Rate Limiter
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate; // tokens per second
this.lastRefillTime = Date.now();
}
refill() {
const now = Date.now();
const timePassed = (now - this.lastRefillTime) / 1000;
const tokensToAdd = timePassed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefillTime = now;
}
consume(tokens = 1) {
this.refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return true;
}
return false;
}
available() {
this.refill();
return Math.floor(this.tokens);
}
}
// Express middleware
const express = require("express");
const app = express();
const rateLimiters = new Map();
const tokenBucketRateLimit = (capacity, refillRate) => {
return (req, res, next) => {
const key = req.user?.id || req.ip;
if (!rateLimiters.has(key)) {
rateLimiters.set(key, new TokenBucket(capacity, refillRate));
}
const limiter = rateLimiters.get(key);
if (limiter.consume(1)) {
res.setHeader("X-RateLimit-Limit", capacity);
res.setHeader("X-RateLimit-Remaining", limiter.available());
next();
} else {
res.status(429).json({
error: "Rate limit exceeded",
retryAfter: Math.ceil(1 / limiter.refillRate),
});
}
};
};
app.get("/api/data", tokenBucketRateLimit(100, 10), (req, res) => {
res.json({ data: "api response" });
});#!/bin/bash
# validate-api.sh - Validate API specification
# Usage: ./validate-api.sh <openapi_spec>
set -euo pipefail
SPEC_FILE="${{1:?Usage: $0 <openapi_spec>}}"
echo "Validating API spec: $SPEC_FILE"
# TODO: Add API validation
# - Validate OpenAPI/Swagger syntax
# - Check endpoint naming conventions
# - Verify response schemas
# - Check for required headers
# - Validate authentication definitions
echo "API validation complete."
# API Endpoint Scaffold
# TODO: Customize for your API framework
openapi: "3.0.3"
info:
title: "API Service"
version: "1.0.0"
paths:
/api/v1/resource:
get:
summary: "List resources"
# TODO: Define parameters and responses
responses:
"200":
description: "Success"
post:
summary: "Create resource"
# TODO: Define request body and responses
responses:
"201":
description: "Created"
Related skills
How it compares
Pick api-rate-limiting when you need algorithm-level design guidance inside your own API codebase rather than outsourcing limits to a cloud WAF alone.
FAQ
Which rate limiting algorithms does api-rate-limiting cover?
api-rate-limiting documents three algorithms: token bucket, sliding window, and fixed window. The skill explains when each fits traffic bursts, steady quotas, or simple time windows on HTTP APIs.
What scopes can API rate limits use?
api-rate-limiting describes per-user, per-IP, and per-endpoint rate limit scopes. Developers can combine scopes to block brute-force login attempts while preserving fair usage on public read endpoints.