Rate Limits
Rate limits protect platform stability and ensure fair access for all organisations. Limits apply per API key.
Limits by plan
| Plan | Requests / minute | Requests / hour | Requests / day | Concurrent connections |
|---|---|---|---|---|
| Standard | 60 | 1,000 | 5,000 | 5 |
| Pro | 300 | 10,000 | 50,000 | 20 |
| Enterprise | 1,000 | 50,000 | Unlimited | 100 |
Limits are applied per API key. If you have multiple keys, each key has its own limit.
Rate limit headers
Every API response includes these headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window |
X-RateLimit-Remaining |
Requests remaining in the current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
X-RateLimit-Window |
Window duration in seconds (60 for per-minute) |
Example:
HTTP/1.1 200 OK
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 247
X-RateLimit-Reset: 1730000060
X-RateLimit-Window: 60
When you exceed the limit
An exceeded rate limit returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
Content-Type: application/json
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the per-minute request limit. Retry after 23 seconds.",
"retryAfter": 23
}
}
The Retry-After header and retryAfter field indicate the number of seconds to wait before retrying.
Implementing back-off
Always implement exponential back-off with jitter in your integration:
async function requestWithBackoff(fn: () => Promise<any>, maxRetries = 5) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
if (err.status === 429 && attempt < maxRetries) {
const retryAfter = parseInt(err.headers['retry-after'] || '1', 10);
const jitter = Math.random() * 1000;
await new Promise(r => setTimeout(r, retryAfter * 1000 + jitter));
} else {
throw err;
}
}
}
}
Optimising request volume
A few patterns that significantly reduce unnecessary requests:
Use webhooks instead of polling
Subscribe to webhooks for emission entry and report events instead of checking for changes on a timer.
Batch reads with pageSize=200
When listing emissions or other resources, use the maximum page size to fetch more in fewer requests.
Cache emission factors
The factor library changes infrequently. Cache GET /api/v1/emission-factors responses for 24 hours.
Use the summary endpoint for dashboards
GET /api/v1/reports/summary returns aggregate metrics in one call — do not aggregate by fetching all entries.
Idempotent retries only
Use Idempotency-Key on POST requests so you can safely retry on timeout without duplicating data (and without counting the retry against your limit if the original request succeeded).
Burst allowance
Plans include a short burst allowance of up to 2× the per-minute limit for up to 10 seconds. This accommodates one-time imports and orchestration spikes. Sustained traffic above the limit will still be throttled.
Increasing limits
Contact sales@alchemos.io if your integration requires higher limits. Enterprise customers can negotiate custom limits per key.