Rate Limits

API requests are rate limited to ensure fair usage and system stability.

Rate Limits by Plan

PlanRequests/minEmails/dayStorage
Platform1,000100K/month*100 GB
EnterpriseCustomCustom*Custom

* Usage-based pricing applies. See pricing for details.

Email-Specific Limits

Limit TypeValueDescription
Batch size100Max emails per batch request
Broadcast recipients1,000Max recipients per broadcast
Email size25 MBMax total email size with attachments
Attachments10Max attachments per email

CDN-Specific Limits

Limit TypeValueDescription
Max file size100 MBMaximum upload file size
Bulk delete100Max assets per delete request
Bulk move100Max assets per move request
Image dimension4096pxMax transform output dimension

Rate Limit Headers

Every API response carries the standard IETF rate-limit headers, so a client can self-throttle without guessing. Read RateLimit-Remaining and RateLimit-Reset and you have everything you need.

HeaderValueDescription
RateLimit-Limit200Requests permitted in the current window
RateLimit-Remaining199Requests still permitted in the current window
RateLimit-Reset60Seconds until the window resets. Not a Unix timestamp.
RateLimit"default";r=199;t=60Current service limit as an RFC 8941 structured field: r remaining, t seconds to reset
RateLimit-Policy"default";q=200;w=60The quota policy itself: q requests per w seconds
Retry-After45Seconds to wait. Present on 429 responses, and always equal to RateLimit-Reset.

The legacy X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers carry the same values and are still sent, so existing clients keep working. New code should read the standard headers.

Handling Rate Limits

When you exceed the rate limit, you'll receive a 429 response:

429 Response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
RateLimit-Limit: 200
RateLimit-Remaining: 0
RateLimit-Reset: 45
RateLimit: "default";r=0;t=45
RateLimit-Policy: "default";q=200;w=60
Retry-After: 45
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please wait 45 seconds before retrying."
}
}

Best Practices

  • 1.Check headers — Monitor RateLimit-Remaining and slow down before you reach zero
  • 2.Use batch endpoints — Send multiple emails in one request instead of many single requests
  • 3.Implement backoff — Use exponential backoff when retrying after rate limit errors
  • 4.Queue requests — For high-volume operations, implement a queue to spread requests over time
  • 5.Cache responses — Cache read operations where possible to reduce API calls

Example: Rate Limit Aware Client

rate-limit-client.ts
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms))
class RateLimitedClient {
private remaining = Number.POSITIVE_INFINITY
private resetAt = 0 // epoch ms
async request(fn: () => Promise<Response>): Promise<Response> {
// Out of quota and the window has not rolled over yet: wait it out.
if (this.remaining === 0 && Date.now() < this.resetAt) {
await sleep(this.resetAt - Date.now())
}
const response = await fn()
// RateLimit-Reset is seconds until reset, not a Unix timestamp.
const remaining = response.headers.get('RateLimit-Remaining')
const reset = response.headers.get('RateLimit-Reset')
if (remaining !== null) this.remaining = Number(remaining)
if (reset !== null) this.resetAt = Date.now() + Number(reset) * 1000
if (response.status === 429) {
// Retry-After always matches RateLimit-Reset on a 429.
const retryAfter = Number(response.headers.get('Retry-After') ?? 60)
await sleep(retryAfter * 1000)
return this.request(fn)
}
return response
}
}

Need Higher Limits?

If you need higher rate limits or custom quotas, contact us about our Enterprise plan. We can accommodate high-volume use cases with dedicated infrastructure.