MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
API Reference

Rate Limits

Per-endpoint rate limits, 429 responses, and Redis-backed headers

MisarMail enforces two types of limits:

  1. Rate limits — requests per short window (burst protection)
  2. 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 keyed by the authenticated user when available, otherwise by client IP. Because most /v1/* endpoints apply the limit before resolving your API key, they are effectively enforced per client IP. POST /v1/send additionally applies a per-API-key burst limit.


Limits by Endpoint

EndpointWindowLimitNotes
POST /v1/send1 minute100 requestsPer IP
POST /v1/send1 hour100 requestsPer API key (burst)
POST /v1/ai/subject-lines1 minute10 requestsPer IP
All other /v1/* (contacts, campaigns, import, validate, templates, …)1 minute100 requestsPer IP

Both limits on POST /v1/send apply — a request must pass the 100/minute check and the 100/hour per-key check.


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 try again later.",
  "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: 42

Use 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://api.misar.io/mail/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: "..." });