Rate Limits
Understanding API rate limits, headers, and best practices for handling rate-limited requests
Lumail enforces rate limits to ensure fair usage and protect service quality for all users. This page explains how rate limiting works and how to handle rate-limited requests.
Rate Limits by Plan
API requests are rate-limited per organization based on your subscription plan:
| Plan | Requests per Minute |
|---|---|
| Free | 100 |
| Premium | 700 |
| Enterprise | 2,000 |
Additionally, all requests are subject to an IP-based rate limit of 500 requests per second to prevent abuse.
Rate Limit Headers
Authenticated REST API responses include the current organization quota. Authentication failures still advertise the minimum API policy so clients can plan before presenting a token.
| Header | Description |
|---|---|
RateLimit-Policy | Named quota and 60-second window |
RateLimit | Remaining requests and seconds until reset |
RateLimit-Limit | Maximum requests allowed per minute |
RateLimit-Remaining | Remaining requests in the current window |
RateLimit-Reset | Seconds until the current window resets |
X-RateLimit-* | Legacy compatibility values for existing clients |
Example headers:
RateLimit-Policy: "lumail-api";q=100;w=60
RateLimit: "lumail-api";r=95;t=42
RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 42
Rate Limit Exceeded Response
When you exceed the rate limit, the API returns a 429 Too Many Requests response:
{
"message": "Too many requests",
"docs": "https://lumail.io/docs/api-reference/api-limits"
}Additional headers on 429 responses:
| Header | Description |
|---|---|
Retry-After | Seconds to wait before retrying |
Handling Rate Limits
Best Practices
- Monitor headers - Track
RateLimit-Remainingto know when you're approaching the limit - Implement backoff - When rate limited, wait for the
Retry-Afterduration before retrying - Batch requests - Combine multiple operations into fewer API calls where possible
- Cache responses - Store frequently accessed data locally to reduce API calls
Exponential Backoff Example
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
if (response.status === 429) {
// Get retry delay from header or calculate exponentially
const retryAfter = response.headers.get("Retry-After");
const waitTime = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.pow(2, i) * 1000;
console.log(`Rate limited. Waiting ${waitTime}ms before retry...`);
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
throw new Error("Max retries exceeded");
}Checking Rate Limit Status
Before making requests, you can check your remaining quota:
const response = await fetch("https://lumail.io/api/v1/tags", {
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
},
});
const limit = response.headers.get("RateLimit-Limit");
const remaining = response.headers.get("RateLimit-Remaining");
const resetAfter = response.headers.get("RateLimit-Reset");
console.log(`${remaining}/${limit} requests remaining`);
console.log(`Resets in ${resetAfter} seconds`);Increasing Your Rate Limit
If you need higher rate limits:
- Upgrade your plan - Premium and Enterprise plans include higher limits
- Contact support - For custom enterprise needs, we can discuss tailored solutions
Common Scenarios
Bulk Operations
For bulk imports or updates, consider:
- Using the
POST /api/v1/subscribersendpoint which handles upserts - Spreading requests over time instead of bursting
- Processing in batches with delays between batches
async function bulkImport(subscribers, batchSize = 50, delayMs = 1000) {
for (let i = 0; i < subscribers.length; i += batchSize) {
const batch = subscribers.slice(i, i + batchSize);
// Process batch in parallel
await Promise.all(
batch.map((sub) =>
fetch("https://lumail.io/api/v1/subscribers", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify(sub),
}),
),
);
// Wait between batches to stay under rate limit
if (i + batchSize < subscribers.length) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
}Webhooks
When receiving webhooks and calling the API:
- Process webhook events asynchronously
- Implement a queue to control request rate
- Use
triggerWorkflows: falsewhen updating subscribers to avoid cascading API calls
Related Documentation
- API Tokens - Authentication setup
- Create Subscriber - Bulk import best practices
- Workflows - Automate without API calls