# Rate Limits (/reference/rest/rate-limits)



Rate limits protect the platform and ensure fair usage across tenants. Limits scale with your authentication level — anonymous exploration is restricted, production keys get full throughput.

Limits by Authentication Level [#limits-by-authentication-level]

| Level | Description                 | Requests / Minute | Burst     | Entity Ops / Request |
| ----- | --------------------------- | ----------------- | --------- | -------------------- |
| L0    | Anonymous                   | 30                | 10        | N/A (read-only)      |
| L1    | Session token               | 100               | 30        | 100                  |
| L2    | API key (`hly_sk_...`)      | 1,000             | 100       | 1,000                |
| L3    | Admin key (`hly_admin_...`) | Unlimited         | Unlimited | 10,000               |

**Requests / Minute** is a rolling window. **Burst** is the maximum number of concurrent requests. **Entity Ops / Request** limits the number of entity operations inside a single `do` or batch call.

Rate Limit Headers [#rate-limit-headers]

Every API response includes rate limit headers:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 994
X-RateLimit-Reset: 1706745600
```

| Header                  | Type    | Description                                    |
| ----------------------- | ------- | ---------------------------------------------- |
| `X-RateLimit-Limit`     | integer | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | integer | Requests remaining in the current window       |
| `X-RateLimit-Reset`     | integer | Unix timestamp when the window resets          |

429 Response [#429-response]

When a rate limit is exceeded, the server returns HTTP `429 Too Many Requests` with a `Retry-After` header:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706745612
Content-Type: application/json

{
  "error": "rate_limited",
  "message": "Rate limit exceeded. Retry after 12 seconds."
}
```

Retry Strategy [#retry-strategy]

Implement exponential backoff with jitter for reliable retry behavior:

```typescript
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options)

    if (response.status !== 429) return response

    const retryAfter = parseInt(response.headers.get('Retry-After') || '1', 10)
    const jitter = Math.random() * 1000
    const delay = retryAfter * 1000 + jitter

    await new Promise(resolve => setTimeout(resolve, delay))
  }

  throw new Error('Max retries exceeded')
}
```

Key principles:

* Always respect the `Retry-After` header value
* Add random jitter (0-1 second) to prevent thundering herd
* Cap retries at 3-5 attempts
* Log rate limit events for monitoring

Batch Requests and Rate Limits [#batch-requests-and-rate-limits]

A single [batch request](/docs/reference/rest/batch) counts as one request toward the rate limit, regardless of how many operations it contains. This makes batching the most efficient approach for bulk operations.

```bash
