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

# Rate limits

> Per-tier rps + rpm caps, headers, what 429 means in practice.

Rate limits are tracked per API key, in Redis, with sliding-window
counters. Every response includes the relevant headers so your client
can back off gracefully.

## Per-tier limits

| Tier                   | Requests / sec | Requests / min | Concurrent backtests | Markets / backtest |
| ---------------------- | -------------: | -------------: | -------------------: | -----------------: |
| **Free**               |              1 |             30 |                    1 |                  5 |
| **Pro** (\$19.90 / mo) |             10 |            300 |                    2 |                 20 |
| **Plus** (\$29 / mo)   |             25 |          1,000 |                    5 |                100 |
| **Enterprise**         |         Custom |         Custom |               Custom |             Custom |

[See pricing →](https://polyquantlab.com/pricing)

## Response headers

Every response (including 429s) carries:

```http theme={null}
X-RateLimit-Limit-Sec:        10
X-RateLimit-Remaining-Sec:    7
X-RateLimit-Reset-Sec:        1
X-RateLimit-Limit-Min:        300
X-RateLimit-Remaining-Min:    288
X-RateLimit-Reset-Min:        43
```

`Remaining-*` falls as you consume, `Reset-*` is the seconds until the
window rolls over. If `Remaining-Sec` hits 0 we return `429 Too Many
Requests` with `Retry-After: <seconds>` on the response.

## Recommended client pattern

```python theme={null}
import asyncio, httpx

async def fetch_with_backoff(client, url, **kw):
    for attempt in range(5):
        r = await client.get(url, **kw)
        if r.status_code != 429:
            return r
        wait = float(r.headers.get("Retry-After", 1.0))
        # Jitter to avoid every client retrying in lockstep.
        await asyncio.sleep(wait + 0.05 * attempt)
    r.raise_for_status()
```

## What counts toward the limit

* Every `/v1/*` HTTP call counts as 1 request, regardless of payload size.
* WebSocket subscriptions count as 1 connection (the messages flowing
  inside don't consume rps).
* Backtest jobs count as 1 for the POST + 1 per poll on the status
  endpoint.

## What does NOT count

* `/health` — anonymous, untracked.
* `/api/audit-public` (the public audit summary) — anonymous, edge-cached.

<Note>
  Hitting the per-second cap is a wider-than-typical client problem. If
  your bot is correctly batching it should mostly stay well under 5 rps.
  If you find yourself architecturally needing more than the Plus tier,
  email [contact@polyquantlab.com](mailto:contact@polyquantlab.com) — we'll
  size an Enterprise plan to your shape.
</Note>
