Rate Limits
Understanding Sendable API rate limits and best practices.
Limits by Plan
| Plan | Requests/Minute | Messages/Hour | Concurrent Sessions |
|---|---|---|---|
| Free | 60 | 100 | 1 |
| Starter | 300 | 1,000 | 3 |
| Pro | 1,000 | 10,000 | 10 |
| Enterprise | Custom | Custom | Custom |
Rate Limit Headers
Every API response includes rate limit headers:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 299
X-RateLimit-Reset: 1709380800| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when limit resets |
Handling Rate Limits
When you exceed the rate limit, you'll receive a 429 status code:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again in 60 seconds.",
"retryAfter": 60
}
}Best Practices
1. Implement client-side throttling
class RateLimiter {
private queue: Array<() => Promise<any>> = []
private running = 0
private maxConcurrent = 5
async add(task: () => Promise<any>) {
if (this.running < this.maxConcurrent) {
this.running++
try {
return await task()
} finally {
this.running--
this.processQueue()
}
}
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
resolve(await task())
} catch (e) {
reject(e)
}
})
})
}
private processQueue() {
if (this.queue.length > 0 && this.running < this.maxConcurrent) {
const next = this.queue.shift()
if (next) this.add(next)
}
}
}2. Use exponential backoff
async function withBackoff(fn: () => Promise<any>, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fn()
} catch (error: any) {
if (error.status === 429) {
const delay = Math.min(1000 * Math.pow(2, i), 30000)
await new Promise(r => setTimeout(r, delay))
continue
}
throw error
}
}
}3. Monitor your usage
Track your rate limit headers to proactively adjust your request patterns.