API Reference
Rate Limits
Per-endpoint rate limits, 429 responses, and Redis-backed headers
Rate Limits
MisarMail enforces two types of limits:
- Rate limits — requests per minute per API key (short-term burst protection)
- Plan limits — total emails / contacts / etc. per day or month (long-term usage enforcement)
This page covers rate limits. See Plan Limits for usage quotas.
Rate limits are enforced per API key, not per IP address.
Limits by Endpoint
| Endpoint | Window | Limit | Notes |
|---|---|---|---|
POST /v1/send | 1 minute | 100 requests | Per API key |
GET /v1/contacts | 1 minute | 100 requests | Per API key |
POST /v1/contacts | 1 minute | 60 requests | Per API key |
POST /v1/contacts/import | 1 minute | 10 requests | Per API key |
POST /v1/campaigns | 1 minute | 30 requests | Per API key |
POST /v1/ai/subject-lines | 1 minute | 10 requests | Per API key |
POST /v1/validate | 1 minute | 30 requests | Per API key |
POST /v1/channels/* | 1 minute | 50 requests | Per API key |
All other /v1/* | 1 minute | 60 requests | Per API key (default) |
429 Response
When a rate limit is exceeded, the API returns:
HTTP/1.1 429 Too Many Requests{
"success": false,
"error": "Too many requests. Please slow down.",
"retryAfter": 42
}retryAfter is the number of seconds until the window resets.
Response Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708790460
Retry-After: 42Use the Retry-After header value to schedule your next request rather than polling.
Handling Rate Limits
Always check for 429 and respect Retry-After:
async function sendWithRateLimit(payload: object) {
const res = await fetch("https://mail.misar.io/api/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MISARMAIL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (res.status === 429) {
const data = await res.json();
const retryAfter = data.retryAfter || 60;
await new Promise(r => setTimeout(r, retryAfter * 1000));
return sendWithRateLimit(payload); // retry once
}
return res.json();
}Bulk Operations
For bulk sending (e.g., campaigns to thousands of contacts), use the campaign API instead of calling POST /v1/send in a loop. Campaigns are processed asynchronously by the MisarMail queue, which handles rate limiting, retry logic, and ISP throttling automatically.
// ❌ Don't do this for bulk
for (const contact of contacts) {
await sendEmail({ to: [contact], ... });
}
// ✅ Use campaigns for bulk
await createAndSendCampaign({ segment_id: "...", template_id: "..." });