Rate Limiting
Understanding and working with API rate limits.
Overview
The Transactional API uses rate limiting to ensure fair usage and platform stability. Rate limits vary by endpoint type and plan tier.
Rate Limit Headers
Every API response includes rate limit information in headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Rate Limits by Plan
Starter Plan
| Endpoint Type | Limit |
|---|---|
| Email sending | 50 req/sec |
| SMS sending | 10 req/sec |
| Read operations | 100 req/sec |
| Batch operations | 5 req/sec |
Growth Plan
| Endpoint Type | Limit |
|---|---|
| Email sending | 200 req/sec |
| SMS sending | 50 req/sec |
| Read operations | 500 req/sec |
| Batch operations | 20 req/sec |
Scale Plan
| Endpoint Type | Limit |
|---|---|
| Email sending | 1,000 req/sec |
| SMS sending | 200 req/sec |
| Read operations | 2,000 req/sec |
| Batch operations | 100 req/sec |
Enterprise
Custom rate limits based on your needs. Contact sales for details.
Endpoint-Specific Limits
Email Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /emails | 100 req/sec | Per second |
POST /emails/batch | 10 req/sec | Per second |
GET /emails/messages | 100 req/sec | Per second |
GET /emails/stats | 10 req/sec | Per second |
SMS Endpoints
| Endpoint | Limit | Window |
|---|---|---|
POST /sms | 100 req/sec | Per second |
POST /sms/batch | 10 req/sec | Per second |
GET /sms/messages | 100 req/sec | Per second |
Forms Endpoints
| Endpoint | Limit | Window |
|---|---|---|
| Management APIs | 100 req/min | Per minute |
| Public submission | 10 req/min | Per IP |
Support Endpoints
| Endpoint | Limit | Window |
|---|---|---|
| Management APIs | 100 req/min | Per minute |
| Widget APIs | 1,000 req/min | Per minute |
Handling Rate Limits
Rate Limit Response
When you exceed the rate limit, you'll receive a 429 Too Many Requests response:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 60 seconds",
"details": {
"retryAfter": 60
}
}
}The response also includes:
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067260SDK Implementations
TypeScript SDK
The SDK includes built-in rate limit handling with automatic retries:
import { Transactional } from 'transactional-sdk';
const client = new Transactional({
apiKey: 'your-api-key',
// Automatic retry on rate limits (default: enabled)
retries: 3,
// Custom retry delay multiplier
retryDelay: 1000, // 1 second base
});
// The SDK automatically handles rate limits
try {
const result = await client.emails.send({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>World</p>',
});
} catch (error) {
// Only throws after all retries exhausted
if (error.code === 'RATE_LIMITED') {
console.log(`Rate limited. Retry after ${error.details.retryAfter}s`);
}
}Manual Rate Limit Handling
import { Transactional, RateLimitError } from 'transactional-sdk';
const client = new Transactional({
apiKey: 'your-api-key',
retries: 0, // Disable automatic retries
});
async function sendWithRetry(payload, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.emails.send(payload);
} catch (error) {
if (error instanceof RateLimitError && attempt < maxRetries - 1) {
const delay = error.retryAfter * 1000;
console.log(`Rate limited, waiting ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}Python SDK
from transactional import Transactional
from transactional.errors import RateLimitError
import time
client = Transactional(
api_key="your-api-key",
# Automatic retry on rate limits (default: enabled)
retries=3,
)
# The SDK automatically handles rate limits
try:
result = client.emails.send(
from_email="sender@example.com",
to="recipient@example.com",
subject="Hello",
html="<p>World</p>",
)
except RateLimitError as error:
# Only raises after all retries exhausted
print(f"Rate limited. Retry after {error.retry_after}s")Manual Rate Limit Handling
from transactional import Transactional
from transactional.errors import RateLimitError
import time
client = Transactional(
api_key="your-api-key",
retries=0, # Disable automatic retries
)
def send_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
try:
return client.emails.send(**payload)
except RateLimitError as error:
if attempt < max_retries - 1:
delay = error.retry_after
print(f"Rate limited, waiting {delay}s")
time.sleep(delay)
continue
raise
# Async version
import asyncio
async def send_with_retry_async(payload, max_retries=3):
for attempt in range(max_retries):
try:
return await client.emails.send(**payload)
except RateLimitError as error:
if attempt < max_retries - 1:
delay = error.retry_after
print(f"Rate limited, waiting {delay}s")
await asyncio.sleep(delay)
continue
raiseBest Practices
1. Implement Exponential Backoff
async function withExponentialBackoff<T>(
fn: () => Promise<T>,
maxRetries = 5
): Promise<T> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error.code === 'RATE_LIMITED' && attempt < maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
const delay = Math.pow(2, attempt) * 1000;
// Add jitter to prevent thundering herd
const jitter = Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay + jitter));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}2. Use Batch Endpoints
Instead of many individual requests, use batch endpoints:
// Bad - 100 requests
for (const recipient of recipients) {
await client.emails.send({ to: recipient, ... });
}
// Good - 1 request
await client.emails.sendBatch(
recipients.map(to => ({ to, ... }))
);3. Monitor Rate Limit Headers
Track remaining requests to proactively slow down:
class RateLimitAwareClient {
private remaining = Infinity;
async send(payload) {
// Wait if we're close to the limit
if (this.remaining < 10) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
const response = await fetch('/api/emails', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Authorization': `Bearer ${this.apiKey}` },
});
// Update remaining from headers
this.remaining = parseInt(
response.headers.get('X-RateLimit-Remaining') || '100'
);
return response.json();
}
}4. Queue High-Volume Operations
For bulk operations, use a queue with controlled concurrency:
import PQueue from 'p-queue';
const queue = new PQueue({
concurrency: 10, // Max parallel requests
intervalCap: 50, // Max requests per interval
interval: 1000, // Interval in ms
});
const recipients = getRecipients(); // 10,000 recipients
const results = await Promise.all(
recipients.map(recipient =>
queue.add(() => client.emails.send({ to: recipient, ... }))
)
);5. Cache Read Results
Reduce read requests by caching responses:
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, any>({
max: 1000,
ttl: 60 * 1000, // 1 minute
});
async function getMessage(messageId: string) {
const cached = cache.get(messageId);
if (cached) return cached;
const message = await client.emails.messages.get(messageId);
cache.set(messageId, message);
return message;
}Rate Limit Quotas
In addition to per-second rate limits, some operations have daily/monthly quotas:
| Resource | Starter | Growth | Scale |
|---|---|---|---|
| Emails/month | 10,000 | 100,000 | Unlimited |
| SMS/month | 1,000 | 10,000 | 100,000+ |
| Form submissions/month | 1,000 | 10,000 | Unlimited |
| API requests/day | 10,000 | 100,000 | Unlimited |
When you exceed a quota, you'll receive a 429 error with code QUOTA_EXCEEDED:
{
"success": false,
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Monthly email quota exceeded",
"details": {
"quota": 10000,
"used": 10000,
"resetsAt": "2024-02-01T00:00:00Z"
}
}
}Need Higher Limits?
If you consistently hit rate limits, consider:
- Upgrading your plan - Higher tiers have increased limits
- Using batch endpoints - Process multiple items per request
- Contacting sales - Enterprise plans offer custom limits
Contact support@usetransactional.com for rate limit increases.