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

# Rate limiting

> How ArcenPay rate-limits API requests — the default 500 requests per minute window, which endpoints enforce it, and how to avoid 429 responses.

ArcenPay rate-limits API-key requests using Arcjet. The limit is enforced **per API key** over a sliding 60-second window.

## Default limit

| Window     | Limit                |
| ---------- | -------------------- |
| 60 seconds | 500 requests per key |

Requests beyond the limit return `429 Too Many Requests`:

```json theme={null}
{ "error": "Rate limit exceeded" }
```

## Which endpoints enforce it

The rate limit is applied on the key-authenticated client-safe endpoints:

| Endpoint                       | Limit     |
| ------------------------------ | --------- |
| `GET /api/v1/check`            | 500 / 60s |
| `GET /api/v1/flags/check`      | 500 / 60s |
| `GET /api/v1/entitlements`     | 500 / 60s |
| `POST /api/v1/usage/consume`   | 500 / 60s |
| `POST /api/v1/api-keys/tokens` | 500 / 60s |

General REST routes (companies, events, payment links, checkout sessions) do not enforce this per-key window today.

<Note>
  Rate-limit enforcement depends on `ARCJET_KEY` being configured in the backend environment. In environments where Arcjet is not configured, the limit is not applied.
</Note>

## Best practices

1. **Cache access decisions.** Entitlement results are stable within a billing period. Common patterns:
   * React SDK hooks already cache for 30 seconds (`useCompanyEntitlements`).
   * For server checks, cache `checkEntitlement`/`checkFlag` results for 30–60 seconds when acceptable.
2. **Batch reads.** Use `GET /api/v1/entitlements` (all features in one call) instead of many `/check` calls.
3. **Retry with backoff.** On `429`, wait and retry. Exponential backoff with a small jitter avoids synchronized retry storms.

```typescript theme={null}
async function checkWithRetry(client, key, companyId) {
  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      return await client.checkEntitlement(key, { id: companyId });
    } catch (error) {
      if (error.statusCode !== 429) throw error;
      const backoff = 100 * Math.pow(2, attempt) + Math.random() * 50;
      await new Promise((resolve) => setTimeout(resolve, backoff));
    }
  }
}
```

4. **Use embed tokens for customer-scoped reads.** Browser reads (hooks, `ArcenEmbed`) authenticate with the customer's access token, not your API key, so they do not consume your server key's quota.

## Related

* [Authentication](/api/authentication) — key types and the endpoint auth matrix
* [Errors](/api/errors) — the `429` error response and handling patterns
