> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chargeexpressltd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits

> Per-key token buckets, the headers that report them, and how to back off.

Rate limits are applied **per API key**, using token buckets. A bucket holds a number of tokens (its burst), each request takes one, and tokens refill continuously at a steady rate. Short bursts are fine as long as your average stays under the refill rate.

| Bucket | Applies to | Refill rate | Burst |
| - | - | - | - |
| Default | Every authenticated request | 120 per minute (2 per second) | 60 |
| Payments | `POST /wallet/fundings`, `POST /invoices/usage/pay`, `POST /webhook_endpoints/{endpointId}/test` | 20 per minute | 10 |

Requests to the payments endpoints take a token from **both** buckets.

If you need more than this, use separate keys for separate systems, cache reads, and rely on [webhooks](/webhooks/overview) instead of polling.

## Response headers

Every authenticated response includes:

| Header | Meaning |
| - | - |
| `RateLimit-Limit` | The bucket's refill rate per minute (`120`, or `20` on the charging and payments endpoints). |
| `RateLimit-Remaining` | Whole tokens left in the bucket after this request. |
| `RateLimit-Reset` | Seconds until the bucket is completely full again. |

On the charging and payments endpoints, these headers describe the stricter bucket.

## When you're limited

You get `429 Too Many Requests` with a `Retry-After` header (in seconds) and this body:

```json theme={null}
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Too many requests. Retry in 3s",
    "request_id": "req_t5w2y8z4a1b7c3d9e6f0g2h4"
  }
}
```

Wait at least `Retry-After` seconds before retrying. A `429` is never stored against an `Idempotency-Key`, so retry with the same key.

```javascript theme={null}
async function withRetry(doRequest, attempts = 5) {
  for (let i = 0; ; i++) {
    const res = await doRequest();
    if (res.status !== 429 && res.status < 500) return res;
    if (i >= attempts - 1) return res;
    const retryAfter = Number(res.headers.get('Retry-After'));
    const backoff = retryAfter > 0 ? retryAfter * 1000 : 2 ** i * 500;
    await new Promise((resolve) => setTimeout(resolve, backoff + Math.random() * 250));
  }
}
```

## Good practice

* Spread batch jobs out instead of firing them all at once; the burst covers spikes, not sustained load.
* Use `limit=100` when walking lists, so you make fewer requests.
* Subscribe to `session.*` webhooks rather than polling `GET /sessions/{sessionId}`.
* Limits are an operational safeguard and may be enforced approximately. Don't design around being able to send exactly the documented number.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.