API reference
Everything for the DepthFeed v3 API — authentication, every endpoint, the response contract, the data model, and the venues behind it. Base URL https://api.depthfeed.com/v3.
Contents▾
Introduction
DepthFeed is historical order-book depth for prediction markets — the full bid/ask ladder on both sides, captured tick by tick, for Polymarket, Kalshi, and Limitless. Not the last trade price, not the top of book — every resting level with its size, at every change. It is the data you need to backtest a fill against the liquidity that was actually there.
It is delivered two ways:
| Mode | What | Best for |
|---|---|---|
| REST API | Market discovery, metadata, latest + historical snapshots, account info. | Live discovery, targeted queries, app integration. |
| Parquet bulk | Whole-day, zstd-compressed columnar files — one per asset / market-type / day. | Backtesting at scale in your own stack. |
Coverage spans 3 venues, 7 assets (btc · eth · sol · xrp · doge · bnb · hype) and the recurring up/down crypto markets each venue runs (5m, 15m, 1h, 4h, 24h, depending on venue).
Quickstart
1. Create a free account and mint an API key from the dashboard. Keys look like df_… and are shown once.
2. Make your first authenticated call:
curl https://api.depthfeed.com/v3/whoami \
-H "Authorization: Bearer df_your_key_here"3. The response is a JSON envelope — your plan, limits, and history window:
{
"data": {
"user_id": "acct_…", "plan": "pro",
"rps": 25, "rpm": 1000, "history_days": 60, "coins": "all"
},
"meta": { "request_id": "req_…", "timestamp": "2026-06-07T02:27:09.532Z" }
}4. Pull the latest BTC up/down markets, then the order-book depth for one of them — see Markets and Snapshots.
Authentication
Every endpoint except /v3/health requires an API key. Pass it either way — both schemes are accepted:
# Authorization header (recommended)
curl https://api.depthfeed.com/v3/btc/markets -H "Authorization: Bearer df_your_key"
# or the X-API-Key header
curl https://api.depthfeed.com/v3/btc/markets -H "X-API-Key: df_your_key"Keys are minted per account from the dashboard and carry a plan that sets your rate limit, history window, and asset/venue access. Keys are stored SHA-256-hashed at rest — the raw value is shown exactly once at creation. Revoking a key in the dashboard takes effect immediately. Quotas are per user, not per key — extra keys do not multiply your limit.
| Failure | Status | code |
|---|---|---|
| No key supplied | 401 | AUTH_MISSING |
| Unknown / revoked / malformed key | 401 | AUTH_INVALID |
Base URL & versioning
All endpoints live under a single versioned base path:
https://api.depthfeed.com/v3TLS is required (Let's Encrypt, HTTP/2). The current API version is v3, built to PolyBackTest parity (same envelope, cursor pagination, and error-code enum) over our superset data (3 venues, 7 assets). Legacy v1/v2 namespaces are not served.
Response format
Every successful response is the same envelope:
{
"data": … , // object (single) or array (list)
"pagination": { … }, // present only on list endpoints
"meta": { "request_id": "req_…", "timestamp": "2026-06-07T02:27:09.532Z" }
}Errors share the shape, with the HTTP status set per code:
{
"error": { "code": "INVALID_COIN", "message": "…", "details": { … } },
"meta": { "request_id": "req_…", "timestamp": "…" }
}Every response — success or error — carries meta.request_id, also returned as the X-Request-Id header. Quote it in support requests.
Errors
Branch on error.code — it is stable. The HTTP status is implied by the code.
| code | HTTP | Meaning |
|---|---|---|
AUTH_MISSING | 401 | No API key on the request. |
AUTH_INVALID | 401 | Key is unknown, revoked, or malformed. |
COIN_NOT_IN_PLAN | 402 | Your plan does not include that coin (e.g. eth on Free). |
ENDPOINT_NOT_IN_PLAN | 402 | Kalshi / Limitless require a paid plan. |
HISTORY_LIMIT_EXCEEDED | 403 | start_time / timestamp is older than your plan window. |
INVALID_COIN | 400 | Coin not one of the seven supported. |
INVALID_CURSOR | 400 | Cursor is malformed. |
INVALID_TIMESTAMP | 400 | Not ms-epoch or ISO-8601. |
BATCH_TOO_LARGE | 400 | More than 10 sub-requests in a batch. |
BATCH_INVALID_PATH | 400 | Sub-request is not a GET /v3/ path (per-sub status). |
MARKET_NOT_FOUND | 404 | No market with that id / slug / ticker. |
SNAPSHOT_NOT_FOUND | 404 | No snapshot for the query. |
ENDPOINT_NOT_FOUND | 404 | Path matches no v3 route. |
RATE_LIMIT_BURST | 429 | Per-second burst exceeded. |
RATE_LIMIT_SUSTAINED | 429 | Per-minute sustained limit exceeded. |
INTERNAL_ERROR | 500 | Unexpected server error — retry, then contact us with the request id. |
403 HISTORY_LIMIT_EXCEEDED carries details.max_history_days and details.oldest_allowed. 429 also sets Retry-After and the X-RateLimit-* headers.
Rate limits
Limits are a per-user token bucket with two windows: a per-second burst and a per-minute sustained cap. Exceeding either returns 429 with the matching code and a Retry-After of 1 second.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your per-second burst limit (rps). |
X-RateLimit-Remaining | Burst tokens left this second. |
Retry-After | Seconds to wait (set on 429). |
X-Request-Id | Echo of meta.request_id. |
See Plans & access for the rps/rpm per tier. Bulk Parquet downloads are not subject to the per-request limit.
Pagination & filters
List endpoints use opaque keyset cursors — O(1) at any depth. Pass ?cursor= from the previous page's pagination.next_cursor; stop when has_more is false.
"pagination": {
"next_cursor": "6274…", // opaque; feed back as ?cursor=
"has_more": true,
"limit": 50,
"count": 50,
"total_count": 1284 // only when ?include_count=true
}Common query parameters
| Param | Type | Notes |
|---|---|---|
limit | int | Markets max 100, snapshots/trades max 1000. Defaults: 50 / 100. |
cursor | string | Opaque keyset cursor from the prior page. |
include_count | bool | Adds total_count (costs a COUNT — opt-in). |
start_time | ts | ms-epoch or ISO-8601. Older than your window → 403. |
end_time | ts | ms-epoch or ISO-8601. |
Timestamps accept either milliseconds-since-epoch (e.g. 1780774485780) or ISO-8601 (e.g. 2026-06-07T02:27:09Z). Every value reaching the database is validated and injection-safe. The plan history window is enforced on every list/snapshot endpoint: an explicit start_time beyond the window returns 403; an omitted one is clamped to the window floor.
Plans & access
Four flat plans. The plan field returned by /v3/whoami is the raw value in the right column.
| Plan | plan | Price | rps | rpm | History | Coins | Venues |
|---|---|---|---|---|---|---|---|
| Explorer | free | $0 | 1 | — | 7 days | BTC only | Polymarket + Binance |
| Quant ★ | pro | $29/mo | 25 | 1,000 | 60 days | All 7 | All — incl. Kalshi + Limitless |
| Desk Lite | scale | $79/mo | 50 | 3,000 | 90 days | All 7 | All — incl. Kalshi + Limitless |
| Desk | enterprise | from $200/mo | 100 | 6,000 | 90 days | All 7 | All + dedicated infra |
Gating is enforced server-side:
- Coins — a non-BTC coin on Free →
402 COIN_NOT_IN_PLAN. - Venues — Kalshi and Limitless on Free →
402 ENDPOINT_NOT_IN_PLAN. - History —
start_timebeyond your window →403 HISTORY_LIMIT_EXCEEDED.
Health & identity
/v3/healthno authLiveness probe, no auth. Returns { status: "ok", service, time }.
/v3/whoamiYour plan, rate limits, history window, and coin access — the fastest way to confirm a key works.
Markets
Polymarket up/down markets per coin, joined to settlement reference (open/close price, winner, volume). {coin} is one of btc · eth · sol · xrp · doge · bnb · hype.
/v3/{coin}/marketsFilters: type (5m/15m/1h/4h/24h), resolved (true/false), plus the common time + pagination params.
/v3/{coin}/markets/{market_id}/v3/{coin}/markets/by-slug/{slug}A single market by id or slug. Example response (per-coin price keys are dynamic, like the reference):
{ "data": {
"market_id": "2456720", "event_id": "566750",
"slug": "btc-updown-5m-1780860300", "market_type": "5m",
"start_time": "…", "end_time": "2026-06-07T19:30:00Z",
"condition_id": "0xb7ce…",
"clob_token_up": "15598…", "clob_token_down": "51823…",
"btc_price_start": null, "btc_price_end": null,
"winner": null, "final_volume": null, "final_liquidity": null, "resolved_at": null
}, "meta": { … } }*_price_*, winner, and resolved_at are null until the market settles and the reference backfills (~12–24 h after settlement).Order-book snapshots
The order-book depth time-series for a market, with the underlying coin price stamped on each snapshot.
/v3/{coin}/markets/{market_id}/snapshotsAdd ?include_orderbook=true to get the full ladder (omitted by default for a lighter payload). Time-windowed + cursor-paginated.
/v3/{coin}/markets/{market_id}/snapshots/at/{timestamp}The single snapshot closest to a timestamp (within ±2 s). {timestamp} is ms-epoch or ISO-8601.
{ "data": [{
"id": "1780774485780", "time": "2026-06-06 19:34:45.780",
"market_id": "2456720", "btc_price": 60606.68,
"price_up": 0.505, "price_down": 0.495,
"orderbook_up": { "bids": [[0.50, 304.25], …], "asks": [[0.51, 30.0], …] },
"orderbook_down": { "bids": [ … ], "asks": [[0.50, 304.25], …] }
}], "pagination": { … }, "meta": { … } }Levels are [price, size]. price_up is the best-bid/ask midpoint; price_down = 1 − price_up. The orderbook_down side is derived as the binary complement of the up book (down = 1 − up, sizes preserved). The coin price is the nearest preceding tick via an ASOF join — exact for 1h/4h/24h, a labeled Binance proxy for 5m/15m.
Binance — spot & futures
The underlying-asset book and trade tape from Binance. {venue} is spot or futures. The book is 20 levels per side.
/v3/{coin}/{venue}/latest/v3/{coin}/{venue}/snapshotsLatest book / paginated book history. Each snapshot: price (mid), bids, asks, timestamp.
/v3/{coin}/{venue}/trades/v3/{coin}/{venue}/trades/latest1-second OHLCV candles with an aggressive buy/sell split — exact, 1:1 with the reference:
{ "data": [{
"id": "1780747762000", "timestamp": "2026-06-06 12:09:22.000",
"price_open": 61097.1, "price_high": 61097.1,
"price_low": 61097.09, "price_close": 61097.1,
"total_volume": 0.01667,
"aggressive_buy_volume": 0.0156, "aggressive_sell_volume": 0.00107,
"num_trades": 3
}], "pagination": { … }, "meta": { … } }Kalshi
Kalshi's crypto contracts — full yes/no depth the reference doesn't carry. Paid plans only. 14 series: 15-minute KX{ASSET}15M and daily KX{ASSET}D for all 7 assets.
/v3/kalshi/marketspaid planFilters: ?coin= (base asset) and ?type= (window). Each market carries market_type (15m/1h/1w/24h).
/v3/kalshi/{ticker}/orderbook/latestpaid planNewest full yes/no book for a ticker. yes/no are [price, size] arrays (prices in 0–1 dollars).
{ "data": {
"ticker": "KXHYPED-26JUN0717-T73.9999", "series": "KXHYPED",
"base_asset": "hype", "market_type": "24h",
"id": "1780…", "timestamp": "…",
"yes": [[0.62, 1400], …], "no": [[0.38, 900], …]
}, "meta": { … } }/v3/kalshi/{ticker}/snapshotspaid planThe historical yes/no depth series for one ticker — same row shape, ordered by receive time, time-windowed + cursor-paginated (include_count opt-in, max 1000). The forward-captured Kalshi book history (~1.5 s poll cadence) — full parity with Polymarket and Limitless /snapshots, so Kalshi depth is backtestable too.
Comparing all three venues at once? Start from the cross-venue screener instead of stitching per-venue calls.
Limitless
Limitless Exchange — a CLOB on Base running recurring up/down crypto markets at 5m and 15m. Depth is captured live over its order-book websocket (so even the fast 5m books are recorded). Paid plans only. {slug} looks like btc-up-or-down-15-min-1780798512077.
/v3/limitless/marketspaid planCurrently-open slots. Filters: ?coin= and ?type= (5m/15m).
/v3/limitless/{slug}/orderbook/latestpaid planNewest L2 depth for a slug (bids descending, asks ascending):
{ "data": {
"slug": "btc-up-or-down-15-min-1780798512077", "condition_id": "0x…",
"base_asset": "btc", "market_type": "15m",
"id": "1780798…", "timestamp": "2026-06-07 02:27:09.532", "midpoint": 0.5,
"bids": [[0.45, 200.0], …], "asks": [[0.46, 120.0], …]
}, "meta": { … } }/v3/limitless/{slug}/snapshotspaid planThe historical depth series for one slug — same row shape, ordered by receive time, time-windowed + cursor-paginated. This is the forward-captured order-book history.
Cross-venue screener
Start here for anything cross-venue. The screener answers the core question — how is the same “up or down” market priced on Polymarket, Kalshi, and Limitless right now? — in a single request, so you never stitch the three per-venue endpoints (with their three different identifiers) together yourself. Plan-aware: on Free you see BTC + Polymarket; the Kalshi and Limitless cells fill in on a paid plan.
/v3/screenerThe grid: one cell per asset with each venue's latest “up” probability, best bid/ask, and settlement reference. A placeholder/empty book reports null rather than a meaningless 0.50.
{ "data": {
"assets": ["btc","eth","sol","xrp","doge","bnb","hype"],
"windows": ["5m","15m","1h","4h","24h"],
"cells": [{ "asset": "btc",
"polymarket": { "up": 0.735, "down": 0.265, "best_bid": 0.73, "best_ask": 0.74, "price_to_beat": 62764.0 },
"kalshi": { "up": 0.71, "down": 0.29, … },
"limitless": { "up": 0.75, "down": 0.25, "slug": "btc-up-or-down-…", … } }],
"paid": true
}, "meta": { … } }/v3/screener/{asset}/{window}Drill into one (asset, window): the full L2 book on every venue side by side (books.polymarket / books.kalshi / books.limitless), plus the underlying spot series and the settlement price_to_beat — the single call a cross-venue backtest or arb screen actually needs.
/v3/overviewno authPer-asset, cross-venue time-series overview (price segments per window) — what powers the in-product overview chart. Public, no key required; select with ?asset=.
Batch
/v3/batchRun up to 10 GET /v3/ requests in one round-trip. Each sub-request is dispatched independently and gets its own status.
curl -X POST https://api.depthfeed.com/v3/batch \
-H "Authorization: Bearer df_your_key" -H "Content-Type: application/json" \
-d '{ "requests": [
{ "id": "a", "method": "GET", "path": "/v3/btc/markets?limit=1" },
{ "id": "b", "method": "GET", "path": "/v3/limitless/markets?type=5m&limit=1" }
] }'Returns { data: [ { id, status, body }, … ] } with an outer 200. Over 10 sub-requests → 400 BATCH_TOO_LARGE; a non-GET or non-/v3/ path gets a per-sub 400 BATCH_INVALID_PATH.
Venues & coverage
Precision comes from sourcing the same upstreams the markets use, so values match by construction.
| Venue | Capture | Windows | Notes |
|---|---|---|---|
| Polymarket | CLOB websocket — every book + price-change event | 5m · 15m · 1h · 4h · 24h | Full depth, event-driven. Settlement open/close from Polymarket's own metadata. |
| Kalshi | Public REST, full-depth poll (~1.5 s) | 15m · daily | Yes/no book, up to 100 levels/side, 7 assets × {15m, 24h}. |
| Limitless | socket.io websocket (orderbookUpdate) | 5m · 15m | CLOB on Base; live push captures the fast 5m books REST can't snapshot. |
| Binance | spot + futures @depth20@100ms + @aggTrade | — | Underlying book + 1s OHLCV, the precision anchor for the up/down markets. |
All 7 assets (btc · eth · sol · xrp · doge · bnb · hype) across every venue that lists them.
Data model & Parquet schema
Timestamps are epoch-millis. exch_ts_ms = exchange time, recv_ts_ms = our receive time (the difference is capture latency). The same columns ship in the bulk Parquet files.
polymarket_book — full order-book snapshots
asset_id, condition_id, slug, base_asset, market_type, exch_ts_ms, recv_ts_ms, hash, bid_prices[], bid_sizes[], ask_prices[], ask_sizes[]
polymarket_changes — deltas for replay between snapshots
asset_id, condition_id, exch_ts_ms, recv_ts_ms, side, price, size
polymarket_markets — one row per token
asset_id, market_id, event_id, condition_id, slug, outcome, base_asset, market_type, clob_token_up, clob_token_down, resolution_source, start_time, end_time, first_seen_ms
polymarket_reference — settlement
slug, base_asset, market_type, start_ms, end_ms, price_to_beat, final_price, winner, final_volume, final_liquidity, resolved_at, captured_ms
binance_book — 20-level book (spot & futures)
venue, symbol, exch_ts_ms, recv_ts_ms, bid_prices[], bid_sizes[], ask_prices[], ask_sizes[]
binance_trades_1s — 1s OHLCV
venue, symbol, second_ms, open, high, low, close, volume, buy_volume, sell_volume, trades, recv_ts_ms
prices — unified underlying-price series
asset, source (binance|chainlink), venue, exch_ts_ms, price
kalshi_book / kalshi_markets
kalshi_book: ticker, series, base_asset, market_type, recv_ts_ms, yes_prices[], yes_sizes[], no_prices[], no_sizes[]
kalshi_markets: ticker, event_ticker, series, base_asset, market_type, title, status, strike, open_time, close_time, first_seen_ms
limitless_book / limitless_markets
limitless_book: slug, condition_id, base_asset, market_type, recv_ts_ms, midpoint, bid_prices[], bid_sizes[], ask_prices[], ask_sizes[]
limitless_markets: slug, condition_id, base_asset, market_type, title, status, expiration_ms, first_seen_ms
Parquet bulk delivery
For backtesting at scale, the full book ships as whole-day, zstd-compressed Parquet — one file per asset, market type, and UTC day, with the exact columns above. Pull a day, replay it locally, no per-request rate limit.
Hot data is retained for a 95-day TTL (covers the 90-day Desk window). Desk plans can take S3/R2 direct bulk delivery. Talk to us for a bulk/backfill arrangement on the contact page.
Code examples
Walk a market's full depth history (Python)
import requests
BASE = "https://api.depthfeed.com/v3"
H = {"Authorization": "Bearer df_your_key"}
# 1. find a market
m = requests.get(f"{BASE}/btc/markets?type=15m&limit=1", headers=H).json()["data"][0]
mid = m["market_id"]
# 2. page through its order-book snapshots
cursor, rows = None, []
while True:
q = f"{BASE}/btc/markets/{mid}/snapshots?include_orderbook=true&limit=1000"
if cursor: q += f"&cursor={cursor}"
page = requests.get(q, headers=H).json()
rows += page["data"]
if not page["pagination"]["has_more"]: break
cursor = page["pagination"]["next_cursor"]
print(len(rows), "snapshots")Latest cross-venue depth for BTC up/down (JavaScript)
const h = { Authorization: "Bearer df_your_key" };
// One call → the full BTC 15m book on all three venues at once.
// (No stitching: the screener is the cross-venue entry point.)
const res = await fetch("https://api.depthfeed.com/v3/screener/btc/15m", { headers: h }).then(r => r.json());
const { books } = res.data; // books.polymarket / books.kalshi / books.limitlessStatus & changelog
What's live, newest first:
- Limitless venue. Markets, latest orderbook, and historical depth snapshots — with 5m + 15m captured live over the order-book websocket.
- Kalshi expanded. 14 series (15-minute + daily for all 7 assets);
market_typeexposed and filterable. - Full v3 API. Polymarket markets + snapshots, Binance spot/futures, batch, cursor pagination, per-plan history enforcement, metered usage.
- Public HTTPS.
https://api.depthfeed.comover TLS.
Order-book depth fills forward from capture start and cannot be back-filled — history matures toward your plan window over calendar time. Questions or a bulk request? Get in touch.