Rate Limiting
API rate limits and throttling policies
Rate Limiting
To ensure fair usage and platform stability, all API endpoints are rate-limited. This document explains the rate limiting policies and how to handle rate limit responses.
Rate Limit Headers
Every API response includes rate limit information in the headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
X-RateLimit-Retry-After | Seconds to wait before retrying (on 429) |
Example response headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1704067200
Content-Type: application/jsonEndpoint-Specific Limits
Authentication Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /api/auth/sign-in | 5 | 15 minutes |
POST /api/auth/sign-up | 3 | 15 minutes |
POST /api/auth/forgot-password | 3 | 1 hour |
POST /api/auth/reset-password | 5 | 15 minutes |
POST /api/auth/verify-email | 10 | 1 hour |
Email API Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /api/email/send | 100 | 1 hour |
POST /api/email/send-batch | 10 | 1 hour |
GET /api/email/inboxes | 200 | 1 hour |
GET /api/email/inboxes/:id/messages | 300 | 1 hour |
POST /api/email/inboxes | 20 | 1 hour |
DELETE /api/email/inboxes/:id | 50 | 1 hour |
POST /api/email/webhooks | 50 | 1 hour |
Billing API Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /api/billing/checkout | 10 | 1 hour |
POST /api/billing/portal | 20 | 1 hour |
GET /api/billing/subscriptions | 100 | 1 hour |
POST /api/billing/cancel | 10 | 1 hour |
Project API Endpoints
| Endpoint | Limit | Window |
|---|---|---|
GET /api/projects | 200 | 1 hour |
POST /api/projects | 50 | 1 hour |
PUT /api/projects/:id | 100 | 1 hour |
DELETE /api/projects/:id | 50 | 1 hour |
Admin API Endpoints
| Endpoint | Limit | Window |
|---|---|---|
GET /api/admin/users | 100 | 1 hour |
GET /api/admin/organizations | 100 | 1 hour |
POST /api/admin/impersonate | 10 | 1 hour |
Webhook Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /api/webhooks/stripe | 1000 | 1 hour |
POST /api/webhooks/email | 500 | 1 hour |
Tier-Based Limits
Rate limits vary by subscription tier:
| Tier | Email Send | API Requests | Webhooks |
|---|---|---|---|
| Free | 100/day | 1,000/day | 100/day |
| Starter | 1,000/day | 10,000/day | 1,000/day |
| Pro | 10,000/day | 100,000/day | 10,000/day |
| Enterprise | Unlimited | Unlimited | Unlimited |
Handling Rate Limits
When you exceed a rate limit, the API returns a 429 Too Many Requests response:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
X-RateLimit-Retry-After: 3600
Content-Type: application/json
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit. Please try again in 3600 seconds.",
"retryAfter": 3600,
"limit": 100,
"window": "1h"
}Exponential Backoff
Implement exponential backoff when retrying:
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const retryAfter = response.headers.get('X-RateLimit-Retry-After');
const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying after ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
throw new Error('Max retries exceeded');
}Rate Limit Monitoring
Track your usage to avoid hitting limits:
class RateLimitMonitor {
private limits: Map<string, { remaining: number; reset: number }> = new Map();
updateFromHeaders(endpoint: string, headers: Headers) {
const remaining = parseInt(headers.get('X-RateLimit-Remaining') || '0');
const reset = parseInt(headers.get('X-RateLimit-Reset') || '0');
this.limits.set(endpoint, { remaining, reset });
if (remaining < 10) {
console.warn(`Low rate limit for ${endpoint}: ${remaining} remaining`);
}
}
shouldThrottle(endpoint: string): boolean {
const limit = this.limits.get(endpoint);
if (!limit) return false;
return limit.remaining < 5 && Date.now() / 1000 < limit.reset;
}
}Best Practices
- Cache responses when appropriate to reduce API calls
- Use batch endpoints for bulk operations
- Implement backoff for all API clients
- Monitor headers to track remaining quota
- Queue requests for non-time-sensitive operations
- Use webhooks instead of polling when possible
Increasing Limits
To request higher rate limits:
- Upgrade to a higher tier plan
- Contact support with your use case
- Provide estimated request volumes
- Enterprise customers can negotiate custom limits
Contact: api-support@yourapp.com