Rate Limits
Understand and handle API rate limiting.
Rate limits protect the API from abuse and ensure fair usage for all users. Requests exceeding the limit receive a 429 Too Many Requests response.
Rate Limit Tiers
| Tier | Requests per Second | Notes |
|---|---|---|
| Standard | 5 req/s | Good for most integrations |
| Pro | 10 req/s (up to 50) | Can be increased on request |
| Enterprise | Custom | Contact sales for higher limits |
API access requires a Standard, Pro, or Enterprise subscription. Rate limits are applied per API key, not per user account. If you need to make many concurrent requests, create multiple keys.
Response Headers
Every API response includes rate limit headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1706745600| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per second |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the limit resets |
Rate Limit Exceeded
When you exceed the rate limit, you'll receive:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Limit: 10 requests per second.",
"details": {
"limit": 10,
"retryAfterMs": 1000
}
},
"request_id": "..."
}The response includes a Retry-After header:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706745601Handling Rate Limits
Simple Backoff
Wait for the Retry-After duration before retrying:
async function makeRequest(url, options) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '1', 10);
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return makeRequest(url, options); // Retry
}
return response.json();
}import time
import requests
def make_request(url, headers, json_data):
response = requests.post(url, headers=headers, json=json_data)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 1))
print(f'Rate limited. Waiting {retry_after}s...')
time.sleep(retry_after)
return make_request(url, headers, json_data) # Retry
return response.json()Exponential Backoff
For production systems, use exponential backoff with jitter:
async function requestWithBackoff(url, options, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response.json();
}
// Exponential backoff with jitter
const baseDelay = Math.pow(2, attempt) * 1000;
const jitter = Math.random() * 1000;
const delay = Math.min(baseDelay + jitter, 32000); // Max 32s
console.log(`Rate limited (attempt ${attempt + 1}). Waiting ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
throw new Error('Max retries exceeded');
}import time
import random
import requests
def request_with_backoff(url, headers, json_data, max_retries=5):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=json_data)
if response.status_code != 429:
return response.json()
# Exponential backoff with jitter
base_delay = (2 ** attempt)
jitter = random.uniform(0, 1)
delay = min(base_delay + jitter, 32) # Max 32s
print(f'Rate limited (attempt {attempt + 1}). Waiting {delay:.1f}s...')
time.sleep(delay)
raise Exception('Max retries exceeded')Request Queuing
For high-volume applications, implement a request queue:
class RateLimitedClient {
constructor(apiKey, maxRequestsPerSecond = 10) {
this.apiKey = apiKey;
this.maxRPS = maxRequestsPerSecond;
this.queue = [];
this.processing = false;
}
async request(endpoint, body) {
return new Promise((resolve, reject) => {
this.queue.push({ endpoint, body, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const { endpoint, body, resolve, reject } = this.queue.shift();
try {
const response = await fetch(`https://cargobloom.io/api/v1/${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
resolve(await response.json());
} catch (error) {
reject(error);
}
// Wait to respect rate limit
await new Promise(r => setTimeout(r, 1000 / this.maxRPS));
}
this.processing = false;
}
}Best Practices
1. Monitor Rate Limit Headers
Track X-RateLimit-Remaining to preemptively slow down:
if (rateLimitRemaining < 2) {
await new Promise(r => setTimeout(r, 1000)); // Pause briefly
}2. Batch Your Workload
If you're processing many routes, spread requests over time:
// Instead of 100 requests at once...
const routes = [...]; // 100 routes
// Process in batches respecting rate limit
const batchSize = 10;
for (let i = 0; i < routes.length; i += batchSize) {
const batch = routes.slice(i, i + batchSize);
await Promise.all(batch.map(route => getBenchmark(route)));
await new Promise(r => setTimeout(r, 1000)); // Wait 1 second between batches
}3. Cache Responses
Market data doesn't change drastically. Cache responses to reduce API calls:
const cache = new Map();
const CACHE_TTL = 15 * 60 * 1000; // 15 minutes
async function getCachedBenchmark(origin, dest) {
const key = `${origin}-${dest}`;
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const data = await getBenchmark(origin, dest);
cache.set(key, { data, timestamp: Date.now() });
return data;
}4. Use Multiple Keys
For parallel processing, create multiple API keys:
const apiKeys = [
process.env.CARGOBLOOM_KEY_1,
process.env.CARGOBLOOM_KEY_2,
process.env.CARGOBLOOM_KEY_3,
];
// Round-robin key selection
let keyIndex = 0;
function getNextKey() {
const key = apiKeys[keyIndex];
keyIndex = (keyIndex + 1) % apiKeys.length;
return key;
}Requesting Higher Limits
Standard users: Default 5 req/s limit applies.
Pro users: Contact support to request higher rate limits (up to 50 req/s).
Enterprise users: Custom rate limits are included. Contact your account manager.
Burst Handling
The rate limiter uses a token bucket algorithm that allows short bursts. You can temporarily exceed the limit, but sustained high traffic will be throttled.
Related
- Error Handling — All error codes including rate limit errors
- Credits & Pricing — Credit limits (separate from rate limits)
Last updated: February 3, 2026