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
Every API response includes rate limit information in the headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per minute |
X-RateLimit-Remaining | Remaining requests in the current window |
X-RateLimit-Reset | Unix timestamp when the rate limit resets |
Example headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067260
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
X-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("X-RateLimit-Limit");
const remaining = response.headers.get("X-RateLimit-Remaining");
const reset = response.headers.get("X-RateLimit-Reset");
console.log(`${remaining}/${limit} requests remaining`);
console.log(`Resets at: ${new Date(reset * 1000).toISOString()}`);
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