Rate limits
The market-data credit budget and per-endpoint costs.
Rate limits keep the full history of trading sessions fast for everyone. The budget is generous for its purpose — interactive trading, charting, research, and ordinary integration work essentially never see a 429. What the budget is not is a download quota: staying under the limit does not make bulk extraction of the historical data acceptable. Read Acceptable use first if you're automating anything.
Acceptable use
The platform exists for trading simulation and strategy research: placing simulated trades, replaying sessions, building and backtesting strategies, and the integration work that supports those. Market-data endpoints are provided so your tools can look at the data in service of that — not to copy the data out.
Bulk extraction of the historical dataset is not permitted, at any pace. Systematically walking the archive of past sessions — every snapshot of every day, session after session — to build a local copy or a derivative dataset is a violation of these terms whether it takes an afternoon or six months. Pacing a crawl to stay under the credit budget does not make it acceptable use; the budget bounds burst load, it does not grant a data license.
What this means in practice:
- Fine: a bot or script that reads today's chain while it trades, a backtest workflow that drills into the sessions it's studying, a research notebook that pulls the handful of dates it's analyzing, a dashboard that polls current data.
- Not fine: a crawler that iterates over all historical dates to archive them, a scheduled job that downloads each new session as it appears with no trading or research attached to it, or several accounts splitting the same extraction between them.
Accounts engaged in bulk extraction may be suspended and their networks may lose access to the platform, without notice. If you believe you have a legitimate need for a large dataset, get in touch instead of scripting around the limits.
How the budget works
Every account has a single leaky bucket of 10,000 credits. Each metered request adds its cost to the bucket, which leaks back toward empty over 24 hours (~0.116 credits per second — a full bucket drains in a day). If a request would push the bucket past 10,000 credits, it's rejected with 429.
The cap is a capacity, not a daily allowance: it's the most in-flight usage you can hold at any one instant, and it refills continuously as it drains. A 429 carries where you stand:
{ "error": "rate_limit_exceeded", "retry_after_seconds": 60, "credits_used": 3030, "credits_cap": 10000 }
credits_used can be well below credits_cap and still 429 — what matters is whether this request's cost fits in the remaining headroom (credits_cap − credits_used). A request that costs 150 credits is rejected at 9,900 used, because 9,900 + 150 > 10,000 even though you're under the cap.
What draws against the bucket
Three kinds of request are metered. Everything else — authentication, simulations, orders, positions, transactions, account management — is free. (Separately from the credit bucket, the unauthenticated /auth/* endpoints carry a small per-IP abuse throttle: a burst of 20 requests refilling at 10 per minute. Normal sign-up and login flows never see it; if tripped, the 429 carries a Retry-After header. Registration itself is also bounded: one account per email inbox — provider aliases of the same mailbox count as the same inbox — and a small daily allowance of new accounts per network, returned as 429 from POST /auth/register.)
Market data
Unauthenticated market-data traffic isn't metered; it can only reach the most recent completed session at 30-second resolution, so there's nothing to throttle. For authenticated accounts, each request costs in proportion to the data it returns:
| Path | What it does | Credits |
|---|---|---|
GET /market-data/strikes/{date} |
Lists the available strikes for a session | 5 |
GET /market-data/historical/{date} |
Returns the index price series for a session | 10 |
GET /market-data/option-chain-snapshots/{timestamp} |
Returns the full option chain at one moment | 10 |
GET /market-data/option-chain-snapshots/{startTime}/{endTime} |
Returns option-chain snapshots over a time range | 5 × snapshots |
The range-snapshot endpoint is priced per snapshot returned, so a wide bulk fetch costs the same per data point as a single lookup — no bulk discount, and no penalty. See the market data API for what each endpoint returns and how to call it.
Strategy session drill-in
GET /strategies/{id}/results/days/{date} costs 10 credits — it replays that whole session through the engine on demand (which is also why it takes a few seconds). Clicking through a strategy's calendar is comfortably inside the budget; only a script hammering hundreds of session drill-ins in a loop will feel the bucket. The portfolio drill-in scales the same way: member count × 10 credits per combined day view.
AI assistant turns
Each message you send to an AI assistant draft costs 50 credits — a turn invokes a language model, the most expensive single thing the platform does. A failed turn is refunded.
On top of the shared credit bucket, the assistant carries its own limits — sized for interactive strategy authoring, not automation — that a normal conversation never reaches:
| Limit | Value | Response when exceeded |
|---|---|---|
| Message size | 4 KB per message | 400/413 {"error":"message_too_long"} |
| In-flight turns per account | 2 at once | 429 {"error":"too_many_active_turns"} |
| Turns per rolling 24 h | 50 | 429 {"error":"assistant_budget_exceeded"} |
| Active drafts | 15 | 409 {"error":"too_many_drafts"} |
| New drafts per rolling 24 h | 10 | 429 {"error":"too_many_drafts"} |
The turn, token, and per-account cost limits all roll on a trailing 24-hour window (like the credit bucket, there's no midnight reset), and each 429 carries a Retry-After header. A later off-topic message in an established conversation gets a short, canned redirect instead of a model turn (it costs nothing); repeatedly sending clearly off-topic requests briefly pauses the assistant (429 {"error":"assistant_cooldown"}). Your current assistant usage is on GET /user as assistant_usage (percent of the day's turn/token/cost limits, with a warning flag at 90%).
Backtest runs cost nothing
Running strategy results consumes no credits: the backtester is protected by a per-user cap of 3 concurrently-executing runs instead. Past the cap, POST /strategies/preview and POST /strategies/{id}/results/update return 429 {"error":"too_many_active_backtests"} — a concurrency signal, not a credit one. Retry with backoff (the web app uses 3 s doubling to 30 s); a slot frees as soon as one of your runs finishes, and saving a strategy never trips it (a save at the cap parks its run and starts it automatically later).
Handling 429
A credit 429 means the bucket would overflow, not that anything is wrong with the request itself. Back off and retry once enough credits have drained — credits leak back at about 417 per hour; for a small market-data request even a few seconds frees enough headroom.
Headers keep you oriented without parsing bodies: every successful metered response carries X-RateLimit-Used and X-RateLimit-Limit, so an integration can track its own headroom request-to-request; the 429 itself carries Retry-After with a suggested wait. (The concurrency 429 too_many_active_backtests above is different: it clears when one of your runs finishes, not on a timer.)
Tips for integrations
- Read the headers, not the calendar.
X-RateLimit-Usedon each response is your live headroom gauge — throttle when it climbs past ~80% instead of counting requests yourself. - Fetch ranges, not loops. One range-snapshot call returning 30 snapshots costs the same as 15 single-snapshot calls for twice the data and a fraction of the requests.
- Pull what your work needs, when it needs it. Fetch the sessions your strategy or analysis is actually looking at rather than pre-downloading history "to have it" — pre-fetching the archive is the pattern Acceptable use prohibits, and the data is always a fast request away.