> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.tryenvoy.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.tryenvoy.ai/_mcp/server.

# Rate Limits

## Limits by endpoint category

Rate limits vary by endpoint type:

| Endpoint category                      | Limit          | Window     |
| -------------------------------------- | -------------- | ---------- |
| **Authentication** (`/auth/*`)         | 10 requests    | per minute |
| **API key operations** (`/api-keys/*`) | 1,000 requests | per minute |
| **General endpoints**                  | 100 requests   | per minute |
| **File uploads**                       | 20 requests    | per minute |

## Response headers

Every response includes rate limit headers so you can track your usage:

| 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 current window resets.                    |
| `Retry-After`           | Seconds to wait before retrying. Only present on `429` responses. |

Example headers on a successful response:

```
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1702656000
```

## Handling 429 responses

When you exceed the limit, the API returns a `429` status with a `Retry-After` header:

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 30 seconds.",
    "param": null
  }
}
```

```
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1702656030
```

## Exponential backoff

Implement exponential backoff to handle rate limits gracefully:

```python
import time
import requests

def make_request(url: str, headers: dict, max_retries: int = 3) -> requests.Response:
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            return response

        retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
        time.sleep(retry_after)

    raise Exception("Max retries exceeded")
```

```typescript
async function makeRequest(url: string, headers: Record<string, string>, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

    if (response.status !== 429) {
      return response;
    }

    const retryAfter = parseInt(response.headers.get("Retry-After") ?? String(2 ** attempt), 10);
    await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
  }

  throw new Error("Max retries exceeded");
}
```

## Best practices

* **Implement exponential backoff.** Always respect the `Retry-After` header and back off progressively on repeated 429s.
* **Cache responses.** Store carrier and load data locally instead of fetching on every request.
* **Use batch endpoints.** `POST /loads/batch` lets you create multiple resources in a single request, reducing total API calls.
* **Monitor remaining quota.** Check `X-RateLimit-Remaining` and throttle proactively before hitting the limit.
* **Use webhooks instead of polling.** Prefer push-based updates over repeated GET requests to reduce unnecessary calls.
* **Stagger requests.** Spread batch operations over time rather than sending them in a burst.