GEX
Public feedInitializing terminal
Usage limits and quotas
API requests are rate-limited to ensure fair usage and service stability. Limits vary by endpoint type and are applied per API key.
| Name | Type | Required | Description |
|---|---|---|---|
| Standard GET endpoints | 60 req/min | Optional | Most GET endpoints including /v1/gex, /v1/dsi, /v1/regime, /v1/levels, /v1/signals, /v1/vex, /v1/cex, /v1/positioning, /v1/darkpool, /v1/vcm, /v1/charm, /v1/surface |
| Compute-heavy GET endpoints | 30 req/min | Optional | Endpoints requiring intensive computation: /v1/flow-forecast, /v1/shadow-gamma, /v1/hedging-bands, /v1/gtbr |
| POST endpoints | 10 req/min | Optional | Mutation and compute endpoints: /v1/snapshot, /v1/portfolio/analyze, /v1/portfolio/stress, /v1/portfolio/optimize |
| Oracle | 50 req/day | Optional | The /v1/oracle/query endpoint has a daily limit due to the cost of AI-powered analysis |
Every API response includes rate limit headers so you can track your usage programmatically:
WebSocket connections have separate limits:
WebSocket connections do not count against the REST API rate limits. They are metered separately by connection count rather than message volume.
When you exceed the rate limit, the API returns HTTP 429 with a JSON body and rate limit headers. The recommended approach is exponential backoff:
1. Read the X-RateLimit-Reset header to determine when the window resets. 2. Wait until the reset timestamp before retrying. 3. If no reset header is available, use exponential backoff: wait 1s, then 2s, then 4s, up to a maximum of 60s. 4. Add jitter (random delay of 0-500ms) to avoid thundering herd problems when multiple clients are rate-limited simultaneously.
import requests
import time
import random
def rate_aware_fetch(url, headers):
"""Fetch with rate limit awareness."""
resp = requests.get(url, headers=headers)
# Log rate limit status
remaining = resp.headers.get("X-RateLimit-Remaining")
limit = resp.headers.get("X-RateLimit-Limit")
print(f"Rate limit: {remaining}/{limit} remaining")
if resp.status_code == 429:
reset = int(resp.headers.get("X-RateLimit-Reset", 0))
wait = max(reset - time.time(), 1)
jitter = random.uniform(0, 0.5)
print(f"Rate limited. Waiting {wait + jitter:.1f}s...")
time.sleep(wait + jitter)
return rate_aware_fetch(url, headers) # retry
resp.raise_for_status()
return resp.json()
# Prefer streaming for real-time data
import websockets, asyncio, json
async def stream_instead_of_poll():
"""Use WebSocket instead of polling to avoid rate limits."""
uri = "wss://api.gexengine.com/ws/v1/stream/SPX?api_key=YOUR_API_KEY"
async with websockets.connect(uri) as ws:
await ws.send(json.dumps({"subscribe": ["gex", "regime"]}))
async for msg in ws:
data = json.loads(msg)
print(f"[{data['channel']}] {data['data']}")