Rate Limits
Understanding and working with API rate limits to ensure reliable service.
Overview
The MsGine API rate limits per API key, so keys in the same account have independent budgets.
The limit
100 requests per minute, per API key.
The window slides: each request is counted for 60 seconds from when it was made, and capacity returns as individual requests age out. 100 requests in one second is within the limit; the 101st within the same 60 seconds returns 429.
Rate Limit Headers
Every API response includes rate limit information in headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000| Header | Description |
|---|---|
X-RateLimit-Limit | Total requests allowed per minute |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when limit resets |
Rate Limit Exceeded
When you exceed the rate limit, you'll receive a 429 Too Many Requests response:
{
"statusCode": 429,
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded"
}Read the retry delay from the Retry-After header rather than the body.
The Retry-After header indicates when you can retry (in seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000060Retry-After is the seconds until the oldest counted request leaves the window, so it is usually well under a minute — not a flat 60.
Handling Rate Limits
Manual Retry Logic
If using the REST API directly, implement retry logic:
async function sendWithRetry(url: string, data: any, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, {
method: 'POST',
headers: {
'X-Api-Key': process.env.MSGINE_API_KEY!,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
if (response.status !== 429) {
return response.json()
}
const retryAfter = parseInt(response.headers.get('Retry-After') || '60')
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
}
throw new Error('Max retries exceeded')
}Check Rate Limit Status
Monitor your rate limit status:
async function checkRateLimit() {
const response = await fetch('https://api.msgine.net/api/v1/account', {
headers: {
'X-Api-Key': process.env.MSGINE_API_KEY!,
}
})
const limit = response.headers.get('X-RateLimit-Limit')
const remaining = response.headers.get('X-RateLimit-Remaining')
const reset = response.headers.get('X-RateLimit-Reset')
console.log(`Rate Limit: ${remaining}/${limit}`)
console.log(`Resets at: ${new Date(parseInt(reset!) * 1000)}`)
}Best Practices
1. Implement Exponential Backoff
async function exponentialBackoff(attempt: number) {
const delay = Math.min(1000 * Math.pow(2, attempt), 10000)
await new Promise(resolve => setTimeout(resolve, delay))
}2. Use Request Queuing
For high-volume applications, implement a request queue:
class RequestQueue {
private queue: Array<() => Promise<any>> = []
private processing = false
private readonly requestsPerMinute = 100
async enqueue<T>(request: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await request()
resolve(result)
} catch (error) {
reject(error)
}
})
if (!this.processing) {
this.process()
}
})
}
private async process() {
this.processing = true
const interval = 60000 / this.requestsPerMinute
while (this.queue.length > 0) {
const request = this.queue.shift()!
await request()
await new Promise(resolve => setTimeout(resolve, interval))
}
this.processing = false
}
}3. Batch Recipients
Send to multiple recipients in a single request instead of multiple requests:
// ✅ Better - Single request for multiple recipients
await client.sms.send({
to: phones,
message: 'Hello!'
})
// ❌ Not optimal - Multiple requests
for (const phone of phones) {
await client.sms.send({ to: phone, message: 'Hello!' })
}4. Cache Responses
Cache responses when appropriate:
const cache = new Map()
async function getAccountInfo() {
const cached = cache.get('account')
if (cached && Date.now() - cached.timestamp < 60000) {
return cached.data
}
const data = await client.getAccount()
cache.set('account', {
data,
timestamp: Date.now()
})
return data
}5. Monitor Usage
Track your rate limit usage:
function checkRateLimitHealth(remaining: number, limit: number) {
const percentage = (remaining / limit) * 100
if (percentage < 10) {
console.error('Critical: Less than 10% rate limit remaining')
} else if (percentage < 25) {
console.warn('Warning: Less than 25% rate limit remaining')
}
}Increasing Rate Limits
To increase your rate limits:
- Upgrade your plan: Higher tiers have higher limits
- Contact sales: Enterprise customers can request custom limits
- Optimize usage: Implement batching and caching
Rate Limit by Endpoint
Different endpoints may have different rate limits:
| Endpoint | Limit (req/min) |
|---|---|
POST /developers/sms | Plan-dependent |
GET /developers/sms/history | Plan-dependent |
GET /account | Plan-dependent |
POST /webhooks | 20 |
Concurrency
Requests are counted individually, not by connection. Parallel requests consume the same budget as sequential ones.
Next Steps
- REST API - API reference and examples
- Authentication - Managing API keys
- Best Practices - Optimization strategies