Documentation

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:

ModeWhatBest for
REST APIMarket discovery, metadata, latest + historical snapshots, account info.Live discovery, targeted queries, app integration.
Parquet bulkWhole-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).

Independent project. Not affiliated with Polymarket, Kalshi, Binance, Chainlink, or Limitless. The one inherent caveat is physics, not a bug: order-book depth cannot be back-filled— the history window fills forward from when capture started, so “60 days” is reached on calendar day 60.

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:

shell
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:

json
{
  "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:

shell
# 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.

FailureStatuscode
No key supplied401AUTH_MISSING
Unknown / revoked / malformed key401AUTH_INVALID

Base URL & versioning

All endpoints live under a single versioned base path:

text
https://api.depthfeed.com/v3

TLS 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:

json
{
  "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:

json
{
  "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.

codeHTTPMeaning
AUTH_MISSING401No API key on the request.
AUTH_INVALID401Key is unknown, revoked, or malformed.
COIN_NOT_IN_PLAN402Your plan does not include that coin (e.g. eth on Free).
ENDPOINT_NOT_IN_PLAN402Kalshi / Limitless require a paid plan.
HISTORY_LIMIT_EXCEEDED403start_time / timestamp is older than your plan window.
INVALID_COIN400Coin not one of the seven supported.
INVALID_CURSOR400Cursor is malformed.
INVALID_TIMESTAMP400Not ms-epoch or ISO-8601.
BATCH_TOO_LARGE400More than 10 sub-requests in a batch.
BATCH_INVALID_PATH400Sub-request is not a GET /v3/ path (per-sub status).
MARKET_NOT_FOUND404No market with that id / slug / ticker.
SNAPSHOT_NOT_FOUND404No snapshot for the query.
ENDPOINT_NOT_FOUND404Path matches no v3 route.
RATE_LIMIT_BURST429Per-second burst exceeded.
RATE_LIMIT_SUSTAINED429Per-minute sustained limit exceeded.
INTERNAL_ERROR500Unexpected 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.

HeaderMeaning
X-RateLimit-LimitYour per-second burst limit (rps).
X-RateLimit-RemainingBurst tokens left this second.
Retry-AfterSeconds to wait (set on 429).
X-Request-IdEcho 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.

json
"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

ParamTypeNotes
limitintMarkets max 100, snapshots/trades max 1000. Defaults: 50 / 100.
cursorstringOpaque keyset cursor from the prior page.
include_countboolAdds total_count (costs a COUNT — opt-in).
start_timetsms-epoch or ISO-8601. Older than your window → 403.
end_timetsms-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.

PlanplanPricerpsrpmHistoryCoinsVenues
Explorerfree$017 daysBTC onlyPolymarket + Binance
Quant pro$29/mo251,00060 daysAll 7All — incl. Kalshi + Limitless
Desk Litescale$79/mo503,00090 daysAll 7All — incl. Kalshi + Limitless
Deskenterprisefrom $200/mo1006,00090 daysAll 7All + 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.
  • Historystart_time beyond your window → 403 HISTORY_LIMIT_EXCEEDED.

Health & identity

GET/v3/healthno auth

Liveness probe, no auth. Returns { status: "ok", service, time }.

GET/v3/whoami

Your 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.

GET/v3/{coin}/markets

Filters: type (5m/15m/1h/4h/24h), resolved (true/false), plus the common time + pagination params.

GET/v3/{coin}/markets/{market_id}
GET/v3/{coin}/markets/by-slug/{slug}

A single market by id or slug. Example response (per-coin price keys are dynamic, like the reference):

json
{ "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.

GET/v3/{coin}/markets/{market_id}/snapshots

Add ?include_orderbook=true to get the full ladder (omitted by default for a lighter payload). Time-windowed + cursor-paginated.

GET/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.

json
{ "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.

GET/v3/{coin}/{venue}/latest
GET/v3/{coin}/{venue}/snapshots

Latest book / paginated book history. Each snapshot: price (mid), bids, asks, timestamp.

GET/v3/{coin}/{venue}/trades
GET/v3/{coin}/{venue}/trades/latest

1-second OHLCV candles with an aggressive buy/sell split — exact, 1:1 with the reference:

json
{ "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": { … } }
Futures trades are currently geo-restricted from our capture region — the endpoint is live and returns a valid empty page; the futures book is unaffected. Spot trades and all books are full.

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.

GET/v3/kalshi/marketspaid plan

Filters: ?coin= (base asset) and ?type= (window). Each market carries market_type (15m/1h/1w/24h).

GET/v3/kalshi/{ticker}/orderbook/latestpaid plan

Newest full yes/no book for a ticker. yes/no are [price, size] arrays (prices in 0–1 dollars).

json
{ "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": { … } }
GET/v3/kalshi/{ticker}/snapshotspaid plan

The 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.

GET/v3/limitless/marketspaid plan

Currently-open slots. Filters: ?coin= and ?type= (5m/15m).

GET/v3/limitless/{slug}/orderbook/latestpaid plan

Newest L2 depth for a slug (bids descending, asks ascending):

json
{ "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": { … } }
GET/v3/limitless/{slug}/snapshotspaid plan

The 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.

GET/v3/screener

The 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.

json
{ "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": { … } }
GET/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.

GET/v3/overviewno auth

Per-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

POST/v3/batch

Run up to 10 GET /v3/ requests in one round-trip. Each sub-request is dispatched independently and gets its own status.

shell
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.

VenueCaptureWindowsNotes
PolymarketCLOB websocket — every book + price-change event5m · 15m · 1h · 4h · 24hFull depth, event-driven. Settlement open/close from Polymarket's own metadata.
KalshiPublic REST, full-depth poll (~1.5 s)15m · dailyYes/no book, up to 100 levels/side, 7 assets × {15m, 24h}.
Limitlesssocket.io websocket (orderbookUpdate)5m · 15mCLOB on Base; live push captures the fast 5m books REST can't snapshot.
Binancespot + futures @depth20@100ms + @aggTradeUnderlying 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)

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)

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.limitless

Status & 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_type exposed 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.com over 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.