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

# Rate Limiting

> Understand CryptoMate's rate limits, the 429 response format, and how to implement a safe retry strategy.

## Overview

CryptoMate enforces rate limits on all API endpoints to ensure fair access across integrations. Two independent limits apply to every request:

* **Per IP** — based on the originating client IP address, always applied.
* **Per company** — based on the company resolved from your `x-api-key` header, applied whenever a valid API key is present.

Both limits must be satisfied for a request to proceed. Exceeding either one triggers a penalty block with exponential backoff.

## Limits

| Scope                 | Limit       | Window   |
| --------------------- | ----------- | -------- |
| Per IP address        | 15 requests | 1 second |
| Per company (API key) | 15 requests | 1 second |

Limits are tracked independently — a single request counts against both your IP quota and your company quota at the same time.

## Exponential backoff penalty

When a limit is exceeded, the API applies a **progressive penalty block** that grows exponentially with each successive violation:

| Violation | Block duration        |
| --------- | --------------------- |
| 1st       | 1 second              |
| 2nd       | 2 seconds             |
| 3rd       | 4 seconds             |
| 4th       | 8 seconds             |
| …         | … (doubles each time) |
| 9th+      | 5 minutes (maximum)   |

While a block is active, all requests from that IP or company are rejected immediately — no quota is consumed. The violation count resets automatically after 1 hour of clean traffic.

## 429 response

When rate limited, the API returns HTTP `429` with a `Retry-After` header indicating how many seconds to wait:

```http theme={null}
HTTP/1.1 429 Too Many Requests
Retry-After: 4
```

```json theme={null}
{
  "error_code": "TOO_MANY_REQUESTS",
  "message": "Rate limit exceeded. Retry after 4 seconds"
}
```

## Retry strategy

Always read the `Retry-After` header and wait that exact duration before retrying:

```javascript theme={null}
async function requestWithBackoff(fn, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fn();
    if (response.status !== 429) return response;
    const retryAfter = parseInt(response.headers.get("Retry-After") ?? "1", 10);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
  throw new Error("Rate limit retries exhausted");
}
```

<Tip>
  If you are running batch operations, add a small pause between requests or process items in groups of 10–12 to stay safely under the 15 req/s ceiling and avoid accumulating violations.
</Tip>

## Excluded paths

The following paths are exempt from rate limiting:

| Path prefix                                                       | Notes                         |
| ----------------------------------------------------------------- | ----------------------------- |
| `/cards/webhook`, `/treasury/webhook`, `/virtual-wallets/webhook` | Webhook ingestion endpoints   |
| `/health`, `/actuator`                                            | Infrastructure health checks  |
| `/swagger-ui`, `/api-docs`                                        | API documentation             |
| `/portal`, `/dashboard`                                           | Portal and dashboard routes   |
| `/geography`, `/geofence`                                         | Geographic lookup endpoints   |
| `/events`, `/mail/inbound`                                        | Event stream and inbound mail |
