Overview
The folksbase API uses Upstash Ratelimit backed by Redis to enforce request limits. There are two limiters, each with a different scope and threshold. Both use a sliding window algorithm, which distributes requests more evenly than fixed windows.Limiters
Global Limiter
Applied to all/api/* routes. Identifies clients by IP address.
| Setting | Value |
|---|---|
| Window | 60 seconds (sliding) |
| Max requests | 100 per window |
| Identifier | Client IP (X-Forwarded-For or X-Real-IP) |
| Redis prefix | folksbase:rl |
Upload Limiter
Applied only toPOST /api/imports (CSV file uploads). This is stricter because uploads are expensive — each one triggers blob storage, CSV parsing, AI column mapping, and background processing.
| Setting | Value |
|---|---|
| Window | 10 minutes (sliding) |
| Max requests | 5 per window |
| Identifier | Authenticated user ID, falling back to IP |
| Redis prefix | folksbase:rl:upload |
Response Headers
Every response from a rate-limited endpoint includes these headers:| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (ms) when the window resets |
| Header | Description |
|---|---|
Retry-After | Seconds until the client can retry |
Rate Limit Exceeded
When either limiter rejects a request, the API returns a429 response:
Why Two Limiters?
The global limiter protects the API from general abuse — bots, scrapers, or runaway scripts. 100 requests per minute is generous for normal usage. The upload limiter exists because CSV imports are resource-intensive. A single upload can trigger:- Blob storage write (up to 200 MB)
- CSV parsing and validation
- AI-powered column mapping via Anthropic
- Background job processing with chunked database inserts
- Email notification on completion
Client Best Practices
- Check
X-RateLimit-Remainingbefore making requests in tight loops - When you receive a
429, wait for the duration specified inRetry-Afterbefore retrying - For bulk operations, use the CSV import feature instead of individual
POST /api/contactscalls - The frontend already handles retries for transient failures (502, 503, 504) with a 1-second delay