Rate Limits
Rate limiting policies, limits by endpoint category, headers, and strategies for handling 429 errors.
Rate Limits
HelloJohn applies rate limits to protect the platform and ensure fair usage. Limits vary by endpoint category, authentication type, and plan.
How Rate Limiting Works
Requests are tracked using a sliding window algorithm per API key and tenant. When the limit is exceeded, the API returns a 429 Too Many Requests error.
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 23 seconds.",
"retry_after": 23
}Rate Limit Headers
Every API response includes the following headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Requests allowed per window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait before retrying (only on 429) |
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 842
X-RateLimit-Reset: 1705399200Limits by Category
Authentication Endpoints
Stricter limits apply to auth endpoints to prevent brute-force attacks.
| Endpoint | Limit | Window |
|---|---|---|
POST /v1/auth/sign-in | 10 | 1 minute per IP |
POST /v1/auth/sign-up | 5 | 1 minute per IP |
POST /v1/auth/magic-link | 5 | 10 minutes per IP |
POST /v1/auth/password/forgot | 5 | 10 minutes per email |
POST /v1/mfa/verify | 5 | 10 minutes per user |
POST /v1/auth/token/refresh | 60 | 1 minute per key |
IP-based limits apply per IP address. User-based limits apply per user ID.
Data API Endpoints
| Tier | Limit | Window |
|---|---|---|
Read (GET) | 1,000 | 1 minute per key |
Write (POST, PATCH, DELETE) | 500 | 1 minute per key |
| Bulk operations | 50 | 1 minute per key |
Webhook Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /v1/webhooks/*/test | 10 | 1 minute per key |
| Delivery retries | 100 | 1 hour per key |
Email Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /v1/email/config/test | 5 | 1 hour per key |
| Template previews | 30 | 1 minute per key |
Limits by Plan
| Plan | Read (per min) | Write (per min) | Auth (per IP/min) |
|---|---|---|---|
| Free | 100 | 30 | 5 |
| Starter | 500 | 150 | 10 |
| Pro | 1,000 | 500 | 20 |
| Enterprise | Custom | Custom | Custom |
Handling Rate Limits
Exponential Backoff
When you receive a 429, implement exponential backoff:
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
if (attempt === maxRetries) {
throw new Error("Rate limit exceeded after max retries");
}
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "1", 10);
const delay = retryAfter * 1000 * Math.pow(2, attempt);
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}Monitor Before Hitting Limits
Check the X-RateLimit-Remaining header and slow down proactively:
async function apiRequest(url: string) {
const response = await fetch(url, { headers: { Authorization: "Bearer sk_live_..." } });
const remaining = parseInt(response.headers.get("X-RateLimit-Remaining") ?? "999", 10);
const reset = parseInt(response.headers.get("X-RateLimit-Reset") ?? "0", 10);
if (remaining < 50) {
const waitMs = (reset * 1000) - Date.now();
if (waitMs > 0) {
console.warn(`Approaching rate limit. Waiting ${waitMs}ms...`);
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
return response.json();
}Batch Requests
For bulk operations, use batch-friendly endpoints instead of looping over individual calls:
// ❌ Slow — N API calls
for (const userId of userIds) {
await fetch(`/v1/users/${userId}`);
}
// ✅ Fast — 1 API call
await fetch("/v1/admin/users/search", {
method: "POST",
body: JSON.stringify({ filters: { ids: userIds } }),
});Rate Limit Exemptions
The following are not counted against rate limits:
- Webhook delivery callbacks (your server receiving events)
- JWKS endpoint (
/.well-known/jwks.json) — cached at CDN edge - Health check endpoint (
/health)
Increasing Limits
Contact support@hellojohn.dev to request higher limits for your plan, or upgrade to Enterprise for custom limits with SLA guarantees.