API  /  Rate limits

Rate limits

Per-minute and per-day caps depend on your add-on tier. Both are rolling-window enforced server-side. When you cross a cap, you get HTTP 429 with a Retry-After header. This page is the contract; reference has a one-table summary.

Tier caps

TierPer minutePer dayBurst tolerance
Pro API6050,000None — rolling 60s window
Trader API300unlimitedNone — rolling 60s window

Caps reset at the boundary, not on a fixed timer. If you make 60 calls in a 30-second burst, you'll need to wait the remaining 30 seconds before any request succeeds.

Per-connection limits

Alongside your tier cap, a few endpoints carry a per-connection limit. It exists to keep one busy connection from crowding out everyone else, and it applies to all traffic from the same address — including any dashboard sessions you have open.

EndpointPer minute, per connection
/api/v1/radar120
/api/v1/history60
/api/v1/heatmap60
/api/v1/premium-map60
/api/v1/greeks, /api/v1/volume, /api/v1/spottier cap only

Whichever limit you reach first is the one that applies. If you spread the same workload across several tickers on these endpoints, this is usually the one you meet before your tier cap. Need it raised for your integration? Tell us — that is a normal request, not an exception.

The 429 response

When you cross a cap, you get a JSON body and a numeric Retry-After header:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 23

{
  "detail": "Rate limit exceeded. Try again in 23 seconds."
}

Retry-After is the number of seconds until your earliest queued request will succeed. Always honor it — if you ignore the header and keep hammering, repeated 429s extend the lockout window.

Polling guidance

The underlying chain refreshes every ~1 second for 0DTE during market hours and ~110 seconds for non-0DTE. Polling faster than that wastes rate limit without giving you fresher data.

Practical recommendations:

Backoff on 429

Honor the Retry-After header. If the header is missing for any reason, fall back to exponential backoff starting at 2 seconds, doubling, capped at 60 seconds.

import os, time, requests

KEY = os.environ["GEXBOARD_API_KEY"]

def get_with_retry(url, params=None, max_attempts=5):
    for attempt in range(max_attempts):
        r = requests.get(url, params=params, timeout=10,
            headers={"Authorization": f"Bearer {KEY}"})
        if r.status_code == 429:
            wait = int(r.headers.get("Retry-After", 2 ** attempt))
            time.sleep(min(wait, 60))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError("Rate limit not clearing after 5 attempts")
# Honor Retry-After then retry once
RESP=$(curl -sw "\n%{http_code}\n%header{Retry-After}" \
  -H "Authorization: Bearer $GEXBOARD_API_KEY" \
  "https://gexboard.com/api/v1/radar?ticker=SPY")
CODE=$(echo "$RESP" | tail -2 | head -1)
RETRY=$(echo "$RESP" | tail -1)

if [ "$CODE" = "429" ]; then
  sleep "${RETRY:-2}"
  curl -H "Authorization: Bearer $GEXBOARD_API_KEY" \
    "https://gexboard.com/api/v1/radar?ticker=SPY"
fi

Daily cap (Pro tier)

Pro keys hit a separate 50,000 requests / day cap measured in UTC. The day rolls over at 00:00 UTC. If you exhaust the daily cap, you'll get 429s with a Retry-After in the seconds-until-midnight-UTC range — back off to a long sleep or pause until next day.

Trader has no daily cap (unlimited daily) so you only need to handle the 60-second window.

Suspension

Repeated abuse — ignoring Retry-After, distributed key sharing across IPs, sustained 429-loops — can trigger automatic key suspension per API Terms § 5. We email the account address before suspending where feasible. To rotate a compromised key: /accountRegenerate.

Need higher limits?

Trader API at 300 req/min covers most quant workflows. If your use case genuinely needs more (high-frequency scanning, multi-tenant research platform, distributed agent system), Enterprise quotes are available: contact@gexboard.com. We respond within 24 business hours.