Skip to main content
/

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.


Was this page helpful?