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
| Tier | Per minute | Per day | Burst tolerance |
|---|---|---|---|
| Pro API | 60 | 50,000 | None — rolling 60s window |
| Trader API | 300 | unlimited | None — 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.
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:
- Real-time bots / alerts — 1 request per second per ticker is the maximum useful rate
- Dashboards / monitoring — 1 request every 5–10 seconds is plenty
- Backtesting / research — pull
/api/v1/historyin batches, don't spam/api/v1/radarin a loop - Multi-ticker workflows — stagger calls; avoid bursting all tickers at second :00 of every minute
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: /account → Regenerate.
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.