--- name: replit-rate-limits description: 'Handle Replit resource limits: KV database caps, deployment quotas, and request throttling. Use when hitting storage limits, managing deployment resources, or implementing rate limiting in your Replit-hosted app. Trigger with phrases like "replit rate limit", "replit throttling", "replit 429", "replit storage limit", "replit quota". ' allowed-tools: Read, Write, Edit version: 1.12.0 license: MIT author: Jeremy Longshore tags: - saas - replit - api - limits - database compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw --- # Replit Rate Limits ## Overview Understand and work within Replit's resource limits: Key-Value Database size caps, Object Storage quotas, deployment compute budgets, and egress allowances. Implement rate limiting in your own app for production safety. ## Prerequisites - Replit account with active Repls - Understanding of your current resource usage - For rate limiting: Express or Flask app ## Replit Platform Limits ### Key-Value Database | Limit | Value | |-------|-------| | Total storage | 50 MiB (keys + values combined) | | Maximum keys | 5,000 | | Key size | 1,000 bytes | | Value size | 5 MiB per value | ### Object Storage (App Storage) | Limit | Value | |-------|-------| | Object size | Configurable per bucket | | Bucket count | Per Repl (auto-provisioned) | | Rate | Throttled at high request volume | ### PostgreSQL | Limit | Value | |-------|-------| | Storage | Plan-dependent (1-10+ GB) | | Connections | Pooled, plan-dependent | | Dev + Prod | Separate databases auto-provisioned | ### Deployments | Resource | Autoscale | Reserved VM | |----------|-----------|-------------| | Scale behavior | 0 to N based on traffic | Always-on, fixed size | | Min cost | Pay per request | $0.20/day (~$6.20/month) | | Max resources | Plan-dependent | Up to 4 vCPU, 16 GiB RAM | | Egress | $0.10/GiB over allowance | $0.10/GiB over allowance | ## Instructions ### Step 1: Monitor KV Database Usage ```typescript // Check how close you are to KV limits import Database from '@replit/database'; async function checkKVUsage() { const db = new Database(); const keys = await db.list(); let totalSize = 0; for (const key of keys) { const value = await db.get(key); const valueSize = JSON.stringify(value).length; totalSize += key.length + valueSize; } const limitMiB = 50; const usedMiB = totalSize / (1024 * 1024); const percentUsed = (usedMiB / limitMiB * 100).toFixed(1); console.log(`KV Usage: ${usedMiB.toFixed(2)} MiB / ${limitMiB} MiB (${percentUsed}%)`); console.log(`Keys: ${keys.length} / 5,000`); if (parseFloat(percentUsed) > 80) { console.warn('WARNING: KV database above 80%. Consider migrating large values to Object Storage.'); } } ``` ### Step 2: Implement App-Level Rate Limiting ```typescript // src/middleware/rate-limit.ts — protect your Replit-hosted API import { Request, Response, NextFunction } from 'express'; interface RateLimitEntry { count: number; resetAt: number; } const store = new Map(); export function rateLimit(opts = { windowMs: 60000, max: 100 }) { return (req: Request, res: Response, next: NextFunction) => { const key = req.headers['x-replit-user-id'] as string || req.ip; const now = Date.now(); const entry = store.get(key); if (!entry || now > entry.resetAt) { store.set(key, { count: 1, resetAt: now + opts.windowMs }); setRateLimitHeaders(res, opts.max, opts.max - 1, now + opts.windowMs); return next(); } entry.count++; const remaining = Math.max(0, opts.max - entry.count); setRateLimitHeaders(res, opts.max, remaining, entry.resetAt); if (entry.count > opts.max) { const retryAfter = Math.ceil((entry.resetAt - now) / 1000); res.set('Retry-After', String(retryAfter)); return res.status(429).json({ error: 'Too many requests', retryAfter, }); } next(); }; } function setRateLimitHeaders(res: Response, limit: number, remaining: number, reset: number) { res.set('X-RateLimit-Limit', String(limit)); res.set('X-RateLimit-Remaining', String(remaining)); res.set('X-RateLimit-Reset', String(Math.ceil(reset / 1000))); } // Clean up expired entries periodically setInterval(() => { const now = Date.now(); for (const [key, entry] of store) { if (now > entry.resetAt) store.delete(key); } }, 60000); ``` ### Step 3: Apply Rate Limiting ```typescript import express from 'express'; import { rateLimit } from './middleware/rate-limit'; const app = express(); // Global: 100 requests per minute app.use(rateLimit({ windowMs: 60000, max: 100 })); // Strict: 10 per minute for write operations app.post('/api/*', rateLimit({ windowMs: 60000, max: 10 })); // Generous: 500 per minute for reads app.get('/api/*', rateLimit({ windowMs: 60000, max: 500 })); ``` ### Step 4: Exponential Backoff for External APIs ```typescript // When your Replit app calls external APIs export async function withBackoff( fn: () => Promise, opts = { maxRetries: 5, baseMs: 1000, maxMs: 30000 } ): Promise { for (let attempt = 0; attempt <= opts.maxRetries; attempt++) { try { return await fn(); } catch (err: any) { if (attempt === opts.maxRetries) throw err; const status = err.status || err.response?.status; if (status && status !== 429 && status < 500) throw err; const delay = Math.min(opts.baseMs * 2 ** attempt, opts.maxMs); const jitter = Math.random() * delay * 0.1; await new Promise(r => setTimeout(r, delay + jitter)); } } throw new Error('Unreachable'); } ``` ### Step 5: Request Queue for Burst Protection ```typescript import PQueue from 'p-queue'; // Limit concurrent requests to external services const queue = new PQueue({ concurrency: 5, // max parallel requests interval: 1000, // per this window intervalCap: 10, // max requests in window }); async function rateLimitedFetch(url: string, opts?: RequestInit) { return queue.add(() => fetch(url, opts)); } ``` ## Error Handling | Error | Cause | Solution | |-------|-------|----------| | KV `Max storage exceeded` | Over 50 MiB | Migrate large values to Object Storage | | KV `Max keys exceeded` | Over 5,000 keys | Archive old data, use prefix namespacing | | 429 from your API | Client hitting your limits | Return `Retry-After` header | | Object Storage throttled | Too many rapid requests | Add client-side request queue | | High egress costs | Large responses | Compress, paginate, or cache at CDN | ## Resources - [Replit Database](https://docs.replit.com/cloud-services/storage-and-databases/replit-database) - [Usage-Based Billing](https://docs.replit.com/billing/about-usage-based-billing) - [p-queue](https://github.com/sindresorhus/p-queue) ## Next Steps For security configuration, see `replit-security-basics`.