Skip to content

API Rate Limits

Learn about mailiam’s API rate limiting system, quotas, and best practices for building resilient applications.

mailiam implements comprehensive rate limiting to ensure fair usage and system stability. Rate limits apply at multiple levels:

  1. Per API Key - Limits per individual API key
  2. Per IP Address - Limits per client IP
  3. Per Endpoint - Specific limits for different endpoints
  4. Account-Level - Overall account quotas
PlanRequests/HourRequests/MinuteBurst Limit
Free1,00050100
Pro25,0005001,000
EnterpriseCustomCustomCustom

Different endpoints have different rate limits based on their resource intensity:

Endpoint CategoryRequests/HourRequests/Minute
Form Submissions (/v1/{domain}/send)1,00060
Instant Forms (/instant/forms)50030
Collections (/collections)20020
Domain Management (/domains)10010
Analytics (/analytics)30030
Admin Operations (/admin/*)505

Every API response includes rate limit information in the headers:

HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1672531200
X-RateLimit-RetryAfter: 3600
X-RateLimit-Scope: api-key
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the time window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the limit resets
X-RateLimit-RetryAfterSeconds to wait before making another request
X-RateLimit-ScopeRate limit scope (api-key, ip, endpoint)

When rate limits are exceeded, the API returns a 429 Too Many Requests response:

{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "API rate limit exceeded",
"details": {
"limit": 1000,
"remaining": 0,
"reset_time": "2024-01-01T15:00:00Z",
"retry_after": 3600,
"scope": "api-key"
}
},
"documentation": "https://docs.mailiam.dev/api/rate-limits"
}
CodeDescriptionAction
RATE_LIMIT_EXCEEDEDGeneral rate limit hitWait and retry
API_KEY_RATE_LIMITAPI key specific limitCheck key quotas
IP_RATE_LIMITIP address limit hitChange IP or wait
ENDPOINT_RATE_LIMITEndpoint specific limitUse different endpoint
ACCOUNT_QUOTA_EXCEEDEDAccount-level quota hitUpgrade plan or wait

Implement exponential backoff for resilient applications:

class mailiamAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.mailiam.dev';
}
async request(endpoint, options = {}, retries = 3) {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
};
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const response = await fetch(url, {
...options,
headers
});
// Check rate limit headers
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const reset = parseInt(response.headers.get('X-RateLimit-Reset'));
if (response.status === 429) {
if (attempt === retries) {
throw new Error('Rate limit exceeded, max retries reached');
}
// Exponential backoff: 2^attempt * 1000ms
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited, retrying in ${delay}ms`);
await this.sleep(delay);
continue;
}
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === retries) throw error;
// Wait before retry
const delay = Math.pow(2, attempt) * 1000;
await this.sleep(delay);
}
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Usage
const mailiam = new mailiamAPI('mlm_sk_live_abc123...');
try {
const response = await mailiam.request('/instant/forms', {
method: 'POST',
body: JSON.stringify({
name: 'Contact Form',
email: 'contact@example.com'
})
});
console.log('Form created:', response);
} catch (error) {
console.error('API request failed:', error);
}

Build a client that proactively manages rate limits:

class RateLimitAwareClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.rateLimits = new Map();
this.queue = [];
this.processing = false;
}
async queueRequest(endpoint, options) {
return new Promise((resolve, reject) => {
this.queue.push({
endpoint,
options,
resolve,
reject
});
if (!this.processing) {
this.processQueue();
}
});
}
async processQueue() {
this.processing = true;
while (this.queue.length > 0) {
const request = this.queue.shift();
try {
// Check if we should wait before making request
await this.waitForRateLimit(request.endpoint);
const response = await this.makeRequest(request.endpoint, request.options);
// Update rate limit tracking
this.updateRateLimits(response.headers);
request.resolve(await response.json());
} catch (error) {
request.reject(error);
}
}
this.processing = false;
}
async waitForRateLimit(endpoint) {
const rateLimit = this.rateLimits.get(endpoint);
if (!rateLimit) return;
if (rateLimit.remaining <= 0) {
const now = Date.now() / 1000;
const waitTime = Math.max(0, rateLimit.reset - now) * 1000;
if (waitTime > 0) {
console.log(`Waiting ${waitTime}ms for rate limit reset`);
await this.sleep(waitTime);
}
}
}
updateRateLimits(headers) {
const limit = headers.get('X-RateLimit-Limit');
const remaining = headers.get('X-RateLimit-Remaining');
const reset = headers.get('X-RateLimit-Reset');
const scope = headers.get('X-RateLimit-Scope');
if (limit && remaining && reset) {
this.rateLimits.set(scope, {
limit: parseInt(limit),
remaining: parseInt(remaining),
reset: parseInt(reset)
});
}
}
async makeRequest(endpoint, options) {
return fetch(`https://api.mailiam.dev${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
}
});
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

Always check rate limit headers and log usage:

const logRateLimit = (headers) => {
const limit = headers.get('X-RateLimit-Limit');
const remaining = headers.get('X-RateLimit-Remaining');
const reset = headers.get('X-RateLimit-Reset');
const resetTime = new Date(reset * 1000);
const usage = ((limit - remaining) / limit * 100).toFixed(1);
console.log(`Rate limit usage: ${usage}% (${limit - remaining}/${limit})`);
console.log(`Resets at: ${resetTime.toISOString()}`);
// Alert if usage is high
if (remaining < limit * 0.1) { // Less than 10% remaining
console.warn('Rate limit nearly exhausted!');
}
};

Combine multiple operations into single requests when possible:

// Instead of multiple individual requests
const forms = [];
for (const formData of formList) {
const form = await mailiam.request('/instant/forms', {
method: 'POST',
body: JSON.stringify(formData)
});
forms.push(form);
}
// Use batch operations
const forms = await mailiam.request('/instant/forms/batch', {
method: 'POST',
body: JSON.stringify({
forms: formList
})
});

Cache API responses to reduce request volume:

class CachedmailiamClient {
constructor(apiKey, cacheTimeout = 300000) { // 5 minutes default
this.apiKey = apiKey;
this.cache = new Map();
this.cacheTimeout = cacheTimeout;
}
async get(endpoint) {
const cacheKey = `GET:${endpoint}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
const response = await this.request(endpoint);
this.cache.set(cacheKey, {
data: response,
timestamp: Date.now()
});
return response;
}
// Cache analytics data (changes infrequently)
async getAnalytics(timeRange) {
return this.get(`/analytics?range=${timeRange}`);
}
}

Instead of repeatedly polling for updates, use webhooks:

// Instead of polling for form submissions
const pollForSubmissions = async () => {
while (true) {
try {
const submissions = await mailiam.request('/instant/forms/abc123/submissions');
// Process submissions
await sleep(30000); // Wait 30 seconds
} catch (error) {
if (error.message.includes('rate limit')) {
console.log('Rate limited, increasing poll interval');
await sleep(300000); // Wait 5 minutes
}
}
}
};
// Use webhooks instead (configured in mailiam dashboard)
app.post('/webhooks/form-submitted', (req, res) => {
// Process form submission webhook
console.log('New submission:', req.body);
res.status(200).send('OK');
});

Prioritize critical requests:

class PriorityQueue {
constructor() {
this.high = [];
this.normal = [];
this.low = [];
}
add(request, priority = 'normal') {
this[priority].push(request);
}
next() {
if (this.high.length) return this.high.shift();
if (this.normal.length) return this.normal.shift();
if (this.low.length) return this.low.shift();
return null;
}
isEmpty() {
return this.high.length === 0 &&
this.normal.length === 0 &&
this.low.length === 0;
}
}
const queue = new PriorityQueue();
// Add critical requests with high priority
queue.add({
endpoint: '/domains/verify',
options: { method: 'POST', body: domainData }
}, 'high');
// Add routine requests with normal priority
queue.add({
endpoint: '/analytics',
options: { method: 'GET' }
}, 'normal');

Avoid duplicate requests:

class DeduplicatedClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.pendingRequests = new Map();
}
async request(endpoint, options) {
const key = `${options.method || 'GET'}:${endpoint}:${JSON.stringify(options.body || {})}`;
// Return existing promise if request is already pending
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key);
}
const promise = this.makeRequest(endpoint, options)
.finally(() => {
this.pendingRequests.delete(key);
});
this.pendingRequests.set(key, promise);
return promise;
}
async makeRequest(endpoint, options) {
// Actual API request implementation
const response = await fetch(`https://api.mailiam.dev${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
...options.headers
}
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
}

Track rate limit usage over time:

class RateLimitMonitor {
constructor() {
this.metrics = [];
}
recordUsage(headers) {
const timestamp = Date.now();
const limit = parseInt(headers.get('X-RateLimit-Limit'));
const remaining = parseInt(headers.get('X-RateLimit-Remaining'));
const used = limit - remaining;
const usage = (used / limit) * 100;
this.metrics.push({
timestamp,
limit,
used,
remaining,
usage
});
// Keep only last 1000 records
if (this.metrics.length > 1000) {
this.metrics = this.metrics.slice(-1000);
}
// Alert on high usage
if (usage > 80) {
this.alert(`High rate limit usage: ${usage.toFixed(1)}%`);
}
}
getAverageUsage(minutes = 60) {
const cutoff = Date.now() - (minutes * 60 * 1000);
const recent = this.metrics.filter(m => m.timestamp > cutoff);
if (recent.length === 0) return 0;
const avgUsage = recent.reduce((sum, m) => sum + m.usage, 0) / recent.length;
return avgUsage;
}
alert(message) {
console.warn(`[RATE LIMIT ALERT] ${message}`);
// Send to monitoring system, Slack, etc.
}
}
const monitor = new RateLimitMonitor();
// Use with API client
const response = await fetch(url, options);
monitor.recordUsage(response.headers);

Automatically adjust request rates based on limits:

class AdaptiveRateClient {
constructor(apiKey) {
this.apiKey = apiKey;
this.requestInterval = 1000; // Start with 1 request per second
this.maxInterval = 10000; // Max 10 seconds between requests
this.minInterval = 100; // Min 100ms between requests
}
async request(endpoint, options) {
const response = await this.makeRequest(endpoint, options);
this.adjustInterval(response.headers);
return response;
}
adjustInterval(headers) {
const remaining = parseInt(headers.get('X-RateLimit-Remaining'));
const limit = parseInt(headers.get('X-RateLimit-Limit'));
const usage = ((limit - remaining) / limit) * 100;
if (usage > 90) {
// High usage - slow down significantly
this.requestInterval = Math.min(this.requestInterval * 2, this.maxInterval);
} else if (usage > 70) {
// Moderate usage - slow down slightly
this.requestInterval = Math.min(this.requestInterval * 1.5, this.maxInterval);
} else if (usage < 30) {
// Low usage - speed up
this.requestInterval = Math.max(this.requestInterval * 0.8, this.minInterval);
}
console.log(`Adjusted request interval to ${this.requestInterval}ms (usage: ${usage.toFixed(1)}%)`);
}
async makeRequest(endpoint, options) {
// Wait between requests
await this.sleep(this.requestInterval);
const response = await fetch(`https://api.mailiam.dev${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
...options.headers
}
});
return response;
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}

Enterprise customers can request custom rate limits:

{
"enterprise_limits": {
"requests_per_hour": 100000,
"requests_per_minute": 2000,
"burst_limit": 5000,
"concurrent_connections": 100,
"custom_endpoints": {
"/custom/webhook": {
"requests_per_minute": 1000
}
}
}
}

Enterprise plans include:

  • Dedicated API servers
  • Custom rate limit configurations
  • Priority support for rate limit adjustments
  • Real-time monitoring dashboards
  • Custom alerting thresholds
Terminal window
# View current API key rate limits
mailiam auth limits
# View account quotas
mailiam account quotas
# Monitor real-time usage
mailiam monitor rate-limits --live
Terminal window
# Batch operations to reduce API calls
mailiam domains setup --all
# Use local caching
mailiam config --cache-duration 300
# Parallel processing with rate limiting
mailiam instant submissions --parallel 5 --rate-limit 10
  1. Unexpected 429 errors

    • Check all active API keys and their usage
    • Verify IP address isn’t hitting limits
    • Review recent API call patterns
  2. Inconsistent rate limits

    • Different endpoints have different limits
    • IP and API key limits are separate
    • Time zone differences in reset times
  3. High rate limit usage

    • Implement request caching
    • Use batch operations
    • Switch to webhooks from polling
Terminal window
# Check rate limit status
curl -H "Authorization: Bearer mlm_sk_live_abc123..." \
https://api.mailiam.dev/rate-limits/status
# View recent rate limit hits
mailiam logs rate-limits --last-hour
# Test rate limiting
mailiam test rate-limits --endpoint /instant/forms --count 100
  • Rate Limit Calculator: Estimate your needs
  • Usage Dashboard: Real-time monitoring
  • API Documentation: Complete endpoint reference
  • Enterprise Support: Custom rate limit solutions

Include this information when requesting rate limit help:

  • API key ID (not the key itself)
  • Current usage patterns
  • Specific endpoints causing issues
  • Business requirements for higher limits
  • Technical architecture details

Proper rate limit management ensures reliable API access while maintaining optimal performance for all mailiam users.