Claude Code Rate Limiting 2026: APIs schützen und Kosten kontrollieren

Ohne Rate Limiting ist jede API ein offenes Buffet — für Bots, fehlerhafte Clients und eigene Programmierfehler. Claude Code kennt die richtigen Algorithmen, implementiert Redis-basierte Middleware für Multi-Server-Setups und hilft dabei, ausgehende API-Calls innerhalb der erlaubten Limits zu halten.

Warum Rate Limiting keine optionale Aufgabe ist

Rate Limiting ist einer jener Aspekte der Backend-Entwicklung, der im Happy Path völlig unsichtbar bleibt — und im Fehlerfall existenzbedrohend werden kann. Eine Produktions-API ohne Rate Limiting ist verwundbar auf mehreren Ebenen gleichzeitig:

API-Schutz: Ein einziger fehlerhafter Client — ein Bug in einer mobilen App, ein Bot, ein falsch konfigurierter Cronjob — kann ohne Begrenzung Tausende von Requests pro Minute absenden und deinen Server in die Knie zwingen. DDoS-Angriffe nutzen exakt dieses Fehlen einer Bremse.

Kosten-Control: Bei APIs die pro Request abrechnen — OpenAI, Anthropic, AWS Lambda, externe Datenprovider — kann ein einziger unkontrollierter Burst eine Monatsrechnung in Minuten verursachen. Kein Klick im Dashboard stoppt das schnell genug. Rate Limiting am Eingang ist der einzige zuverlässige Schutz.

Fair-Use: In Multi-Tenant-Systemen muss sichergestellt werden, dass ein einzelner Nutzer oder eine Organisation nicht die gesamte Kapazität verbraucht. Rate Limiting schafft faire Nutzungsbedingungen — und gibt dir einen klaren Hebel für Preismodelle: Gratisplan = 100 Requests/Minute, Pro-Plan = 1.000 Requests/Minute.

Was dich dieser Artikel kostet ohne ihn zu lesen: Eine ungebremste externe API kostet im Schnitt 3–15× mehr als nötig, wenn Retry-Loops und parallele Requests nicht begrenzt werden. Ein echter Incident in 2025 hat einem Startup €4.200 OpenAI-Kosten in einer Nacht verursacht — ausgelöst durch einen Bug in einem Retry-Loop ohne Backoff.

1. Die drei Rate-Limiting-Algorithmen — wann welcher?

Claude Code erklärt nicht nur Code, sondern hilft bei der Algorithmus-Wahl. Die drei verbreitetsten Ansätze haben grundlegend unterschiedliche Eigenschaften:

Algorithmus Wie es funktioniert Bursts Memory Einsatz
Fixed Window Zähler pro Zeitfenster (z.B. 100/min). Reset am Fenster-Ende. Erlaubt Doppel-Burst O(1) Einfache APIs, interne Tools
Sliding Window Zählt Requests im gleitenden Zeitfenster. Kein harter Reset. Kontrolliert O(n) Requests Produktions-APIs, Multi-Tenant
Token Bucket Eimer mit Tokens, die sich über Zeit auffüllen. Request = 1 Token. Definierter Burst O(1) Ausgehende API-Calls, Bandwidth-Control

Fixed Window ist das Einfachste — aber hat ein bekanntes Problem: Am Grenzbereich zwischen zwei Fenstern können doppelt so viele Requests wie erlaubt durchkommen. Um 23:59:30 Uhr 100 Requests, um 00:00:01 Uhr nochmal 100 Requests — beide Male innerhalb des jeweiligen Limits.

Sliding Window löst dieses Problem elegant: Das Zeitfenster gleitet mit jedem Request mit. Es ist die beste Wahl für Public APIs mit fairem Multi-Tenant-Betrieb — kostet aber etwas mehr Speicher in Redis.

Token Bucket erlaubt definierte Bursts: Ein Eimer mit 20 Tokens füllt sich mit 5 Tokens pro Sekunde wieder. Ein Client darf kurz 20 Requests auf einmal schicken (Burst), danach ist er auf 5/Sekunde gedrosselt. Ideal für ausgehende API-Calls wo du dich an die Limits eines Drittanbieters halten musst.

Häufiger Fehler: Fixed Window für Public APIs zu verwenden, weil es "einfacher" ist. Bei hohem Traffic mit vielen parallelen Clients führt der Doppel-Burst-Effekt zu genauso vielen Überlastungen wie gar kein Rate Limiting. Sliding Window ist der richtige Standard.

2. Rate Limiting für eigene APIs — Express + Redis

Die einfachste Absicherung einer Express-API liefert express-rate-limit. Für Single-Server-Setups reicht der In-Memory-Store, für Multi-Server-Deployments (horizontal skaliert, Kubernetes, mehrere Prozesse) muss Redis her.

Schritt 1: express-rate-limit installieren und konfigurieren

# Installation npm install express-rate-limit rate-limit-redis ioredis # Für TypeScript-Projekte zusätzlich: npm install -D @types/express
// rateLimiter.ts — Basis-Konfiguration für Express import rateLimit from 'express-rate-limit'; export const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 Minuten max: 100, // max 100 Requests pro Fenster standardHeaders: true, // X-RateLimit-* Headers setzen legacyHeaders: false, // X-RateLimit-Limit deaktivieren (veraltet) message: { status: 429, error: 'Too Many Requests', retryAfter: 'Bitte warte 15 Minuten bevor du es erneut versuchst.' }, // Vertrauenswürdige Proxies: IP aus X-Forwarded-For nehmen keyGenerator: (req) => req.ip ?? req.socket.remoteAddress ?? 'unknown' }); // Strengeres Limit für Auth-Endpunkte (Brute-Force-Schutz) export const authLimiter = rateLimit({ windowMs: 60 * 60 * 1000, // 1 Stunde max: 10, message: { status: 429, error: 'Zu viele Login-Versuche. Konto für 1 Stunde gesperrt.' } }); // app.ts: Middleware einbinden app.use('/api/', apiLimiter); app.use('/auth/', authLimiter);

Schritt 2: Redis-basiertes Sliding Window für Multi-Server

Sobald deine App auf mehreren Prozessen oder Servern läuft, zählt jeder In-Memory-Store für sich allein — ein Client kann die Grenzen n-fach ausnutzen. Redis als zentraler Store löst das Problem:

// redisRateLimiter.ts — Sliding Window mit Redis import rateLimit from 'express-rate-limit'; import { RedisStore } from 'rate-limit-redis'; import Redis from 'ioredis'; const redis = new Redis({ host: process.env.REDIS_HOST ?? 'localhost', port: parseInt(process.env.REDIS_PORT ?? '6379'), password: process.env.REDIS_PASSWORD }); export const slidingWindowLimiter = rateLimit({ windowMs: 60 * 1000, // 1 Minute gleitendes Fenster max: 60, standardHeaders: true, legacyHeaders: false, store: new RedisStore({ sendCommand: (...args: string[]) => redis.call(...args), prefix: 'rl:' // Redis-Key-Prefix zur Unterscheidung }), // Per-Tenant Rate Limiting: API Key aus Header extrahieren keyGenerator: (req) => { const apiKey = req.headers['x-api-key']; return typeof apiKey === 'string' ? apiKey : req.ip ?? 'anon'; } });
Warum Redis-Store statt In-Memory? Mit 3 App-Instanzen und In-Memory-Store könnte ein Client 3 × 60 = 180 Requests pro Minute machen. Redis als shared Store stellt sicher, dass alle Instanzen denselben Zähler sehen — unabhängig von Load Balancer oder Auto-Scaling.

3. Ausgehende API-Calls begrenzen — Claude API Limits einhalten

Rate Limiting geht nicht nur eingehend. Wer externe APIs nutzt — besonders KI-APIs wie Claude oder GPT mit engen Tokenlimits — muss auch ausgehende Requests begrenzen. Zwei Libraries machen das einfach:

p-limit: Parallele Requests begrenzen

p-limit ist das einfachste Tool für Concurrency-Begrenzung: Du definierst, wie viele Promises gleichzeitig aktiv sein dürfen.

// claudeBatch.ts — Claude API Calls mit p-limit import pLimit from 'p-limit'; import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic(); const limit = pLimit(3); // Max 3 parallele Claude-Requests async function processDocumentsBatch(documents: string[]): Promise<string[]> { const tasks = documents.map((doc) => limit(async () => { const response = await client.messages.create({ model: 'claude-opus-4-5', max_tokens: 1024, messages: [{ role: 'user', content: `Summarize: ${doc}` }] }); return response.content[0].type === 'text' ? response.content[0].text : ''; }) ); return Promise.all(tasks); } // Verarbeitet 50 Dokumente, aber nie mehr als 3 gleichzeitig const results = await processDocumentsBatch(myDocuments);

Bottleneck: Scheduling mit Requests per Second

Wenn du nicht nur Concurrency, sondern echte Requests-per-Second steuern musst, ist bottleneck die richtige Library. Sie arbeitet mit Token-Bucket-Logik unter der Haube:

// scheduler.ts — Bottleneck für RPS-basiertes Throttling import Bottleneck from 'bottleneck'; const limiter = new Bottleneck({ maxConcurrent: 5, // Parallele Jobs minTime: 200, // Mindestabstand: 200ms = max 5 RPS reservoir: 60, // Token-Bucket: 60 Tokens initial reservoirRefreshAmount: 60, // 60 Tokens auffüllen reservoirRefreshInterval: 60 * 1000 // alle 60 Sekunden }); // Wrapped API-Call — wird automatisch gedrosselt const throttledFetch = limiter.wrap(async (url: string) => { const res = await fetch(url); return res.json(); }); // Alle Calls laufen durch den Limiter — kein manuelles Tracking const results = await Promise.all( urls.map(url => throttledFetch(url)) );
p-limit vs. Bottleneck: p-limit ist perfekt wenn du Concurrency begrenzen willst (max N gleichzeitig). Bottleneck ist die richtige Wahl wenn du Zeit-basiertes Throttling brauchst (max N Requests pro Sekunde/Minute) oder komplexere Scheduling-Logik mit Reservoirs und Prioritäten.

4. Rate Limit Headers richtig setzen

Headers sind die Sprache zwischen deiner API und ihren Clients. Korrekte X-RateLimit-* Headers ermöglichen es Clients, ihr Verhalten anzupassen — bevor es zur 429-Antwort kommt.

// rateLimitHeaders.ts — Manuelle Header-Implementierung import { Request, Response, NextFunction } from 'express'; interface RateLimitInfo { limit: number; remaining: number; resetAt: Date; retryAfter?: number; // Sekunden } function setRateLimitHeaders(res: Response, info: RateLimitInfo): void { // Standard RateLimit Headers (RFC 6585 + IETF Draft) res.set('X-RateLimit-Limit', info.limit.toString()); res.set('X-RateLimit-Remaining', Math.max(0, info.remaining).toString()); res.set('X-RateLimit-Reset', Math.floor(info.resetAt.getTime() / 1000).toString()); // Bei 429: Retry-After Header setzen (Pflicht per RFC 7231) if (info.retryAfter !== undefined) { res.set('Retry-After', info.retryAfter.toString()); } } // Middleware-Beispiel export function rateLimitMiddleware( req: Request, res: Response, next: NextFunction ): void { const info = getRateLimitInfo(req); // aus Redis/Memory holen setRateLimitHeaders(res, info); if (info.remaining <= 0) { res.status(429).json({ error: 'Too Many Requests', retryAfter: info.retryAfter, message: `Rate Limit überschritten. Bitte warte ${info.retryAfter}s.` }); return; } next(); }
Header Bedeutung Beispielwert
X-RateLimit-Limit Maximale Requests im Zeitfenster 100
X-RateLimit-Remaining Verbleibende Requests in diesem Fenster 42
X-RateLimit-Reset Unix Timestamp: wann das Fenster resettet 1746181200
Retry-After Sekunden bis nächster erlaubter Request (bei 429) 30
Wichtig: Retry-After ist kein Nice-to-have — es ist Pflicht gemäß RFC 7231 wenn du 429 zurückgibst. Ohne diesen Header wissen Clients nicht, wann sie es erneut versuchen sollen, und landen in Retry-Loops die deine API weiter belasten.

5. 429-Fehler gracefully behandeln — Retry + Exponential Backoff

Auf der Client-Seite ist ein 429 kein fataler Fehler — es ist ein Signal: "Warte kurz, dann nochmal." Exponential Backoff mit Jitter ist der Industriestandard für automatisches Retry-Verhalten.

// retryWithBackoff.ts — Exponential Backoff mit Jitter interface RetryOptions { maxRetries?: number; baseDelayMs?: number; maxDelayMs?: number; jitter?: boolean; } async function retryWithBackoff<T>( fn: () => Promise<T>, options: RetryOptions = {} ): Promise<T> { const { maxRetries = 5, baseDelayMs = 1000, maxDelayMs = 30000, jitter = true } = options; let attempt = 0; while (true) { try { return await fn(); } catch (error: any) { const is429 = error?.status === 429 || error?.response?.status === 429; if (!is429 || attempt >= maxRetries) { throw error; // Nicht-429 oder Max Retries: sofort werfen } attempt++; // Retry-After Header auswerten wenn vorhanden const retryAfterHeader = error?.response?.headers?.['retry-after']; let delay: number; if (retryAfterHeader) { delay = parseInt(retryAfterHeader) * 1000; } else { // Exponential Backoff: 1s, 2s, 4s, 8s, 16s... delay = Math.min(baseDelayMs * Math.pow(2, attempt - 1), maxDelayMs); } // Jitter: ±20% Zufallsabweichung verhindert Thundering Herd if (jitter) { delay = delay * (0.8 + Math.random() * 0.4); } console.warn(`Rate limited (Versuch ${attempt}/${maxRetries}). Warte ${Math.round(delay)}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); } } } // Einsatz mit Claude API const response = await retryWithBackoff( () => client.messages.create({ model: 'claude-opus-4-5', max_tokens: 512, messages }), { maxRetries: 4, baseDelayMs: 2000 } );

Thundering Herd Problem

Ohne Jitter würden alle Clients nach demselben Backoff-Interval gleichzeitig einen Retry starten — und denselben Server erneut überlasten. Der ±20%-Jitter im Beispiel oben verteilt die Retries zeitlich und verhindert diese Welle. Bei sehr hohem Traffic empfiehlt sich Full Jitter (Math.random() * delay) statt Equal Jitter.

429-Fehler in React/Frontend gracefully anzeigen

// apiClient.ts — Zentraler API-Client mit 429-Handling async function apiCall<T>(url: string, options?: RequestInit): Promise<T> { const response = await fetch(url, options); if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); const waitSeconds = retryAfter ? parseInt(retryAfter) : 60; throw new RateLimitError( `Zu viele Anfragen. Bitte warte ${waitSeconds} Sekunden.`, waitSeconds ); } if (!response.ok) { throw new Error(`API Error: ${response.status} ${response.statusText}`); } return response.json() as T; } class RateLimitError extends Error { constructor(public message: string, public retryAfterSeconds: number) { super(message); this.name = 'RateLimitError'; } }

6. Claude Code Prompt-Templates für Rate Limiting

Diese Prompts kannst du direkt in Claude Code eingeben — sie liefern vollständige, sofort einsetzbare Implementierungen:

  • "Füge Redis-basiertes Sliding Window Rate Limiting zu dieser Express-API hinzu. Nutze per-API-Key Limits mit 100 Requests/Minute für Free-Tier und 1000/Minute für Pro-Tier."
  • "Implementiere einen Token-Bucket-Algorithmus in TypeScript der meine Claude-API-Calls auf 5 Requests pro Sekunde begrenzt."
  • "Schreibe eine retryWithBackoff-Funktion mit Exponential Backoff, Jitter und Retry-After-Header-Auswertung für alle meine API-Calls."
  • "Analysiere diese Express-Route und sag mir welche Endpunkte Rate Limiting brauchen und welche Limits sinnvoll sind."
  • "Baue ein Rate-Limit-Dashboard als /admin/rate-limits Endpoint der aktuelle Nutzung, Top-Consumer und Throttle-Events anzeigt."
  • "Erstelle Jest-Tests für meine Rate-Limit-Middleware — teste Normal-Flow, Limit-Überschreitung, Header-Korrektheit und Redis-Fehlerfall."

Fazit: Rate Limiting ist Infrastructure, kein Feature

Rate Limiting ist kein nachträgliches Add-on — es gehört zur Grundinfrastruktur jeder produktionsfähigen API. Ohne es sind Kosten unkontrollierbar, Server verwundbar und faire Nutzung unmöglich. Die gute Nachricht: Mit den richtigen Libraries ist die Implementierung überschaubar.

Die Algorithmus-Wahl folgt einer einfachen Regel: Fixed Window für interne Tools und Prototypen, Sliding Window für Public APIs mit Multi-Tenant-Anforderungen, Token Bucket für ausgehende Calls an APIs mit engen Limits. Redis als Store skaliert horizontal, express-rate-limit macht die Integration in Express trivial.

Auf der Client-Seite ist Exponential Backoff mit Jitter der Standard — kein Circuit Breaker, kein komplexes Retry-Framework. Eine sauber implementierte retryWithBackoff-Funktion löst 95% der Retry-Anforderungen und verhindert das Thundering-Herd-Problem automatisch.

Zusammenfassung: Sliding Window + Redis für eingehende Public-API-Requests, p-limit oder Bottleneck für ausgehende KI-API-Calls, korrekte X-RateLimit-* Headers für Client-Transparenz, Exponential Backoff mit Jitter für graceful 429-Handling. Claude Code implementiert alle vier Schichten auf Anfrage vollständig und produktionsfertig.

Rate Limiting mit Claude Code in Minuten implementieren

Redis-Middleware, Token Bucket, Retry-Logik — Claude Code generiert produktionsreife Implementierungen für dein Backend. 14 Tage kostenlos testen, keine Kreditkarte nötig.

Jetzt kostenlos starten →