Rate limits
The market-data credit budget and per-endpoint costs.
Rate limits keep the full historical archive fast for everyone by stopping bulk scrapers from crowding out normal traffic. The budget is deliberately generous — interactive trading, charting, research, and ordinary integration work essentially never see a 429; this page only matters if you pull large amounts of data in tight automated loops.
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.)
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.
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. Metered responses also include a Retry-After header and an X-RateLimit-Used / X-RateLimit-Limit pair so an integration can track its own headroom request-to-request. (The concurrency 429 too_many_active_backtests above is different: it clears when one of your runs finishes, not on a timer.)