Skip to main content
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

TierRequests / secRequests / minConcurrent backtestsMarkets / backtest
Free13015
Pro ($19.90 / mo)10300220
Plus ($29 / mo)251,0005100
EnterpriseCustomCustomCustomCustom
See pricing →

Response headers

Every response (including 429s) carries:
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.
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.
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 — we’ll size an Enterprise plan to your shape.