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 — recorded full bid/ask ladders on both sides for Polymarket, Kalshi, and Limitless. Not just the last trade price or top of book: each stored observation includes resting levels and sizes. It is the data you need to backtest a fill against the liquidity that was actually there.

It is delivered over a metered REST API — market discovery, metadata, and latest + historical order-book snapshots, keyset-paginated with separate general-admission and historical-workload controls. Pull exactly the depth your strategy needs and replay it in your own stack.

Order-book 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).

Prices are the second half of the product. /v3/bars serves OHLCV price bars for US-listed equities and ETFs, crypto spot pairs and perpetual futureson every plan, at one-minute base resolution. They share the clock, the schema and the plan history window with the books above, so a strategy can read an equity and act on a contract without reconciling two vendors’ timestamps.

The Desk plan also bundles realtime sports— live Polymarket & Kalshi books, sportsbook odds, scores and injuries across the major leagues. Jump to the Sports endpoints.

New to evaluating data sources? The buyer’s guide covers the five criteria that decide whether prediction-market data is actually backtestable, and how DepthFeed clears each.

Independent project. Not affiliated with Polymarket, Kalshi, Binance, Chainlink, or Limitless. Our own capture runs live, and licensed historical depth makes the longest-running Polymarket series available from January 2026. Exact per-series archive floors are listed under History & coverage. Every plan window is backed by stored data. Third-party data attribution is listed in the terms.

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": 30, "coins": "all", "sports": false,
    "bars": { "max_symbols_per_request": 8 }
  },
  "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 and /v3/overview 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"
/v3/streamalso requires a key, but authenticates once at connection time — a WebSocket can’t carry a per-request header, so it accepts X-API-Key, Authorization: Bearer, or a ?api_key= query param. See Live stream.

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
Key store temporarily unreachable503AUTH_UNAVAILABLE

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_PLAN402Limitless, Predict.fun and live Kalshi depth require a paid plan. Kalshi HISTORY does not — it is bounded by your plan window like Polymarket's.
SYMBOL_LIMIT_EXCEEDED402More symbols in one /v3/bars request than your plan's batch size (3 / 8 / 16 / 32). Split the list and re-request — this bounds ONE call, not how many symbols you may pull.
WINDOW_TOO_WIDE400One /v3/bars request spans more time than the frame's row count allows (45 days at 1Min, 180 at 5Min, a year at 15Min and 1Hour, ten years at 1Day). Page with limit and cursor.
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.
INVALID_INTERVAL400interval not a duration like 30s, 1m, 5m, 1h, 1d (1s–1d).
INVALID_SLUG400Empty/invalid {slug} path segment (e.g. a missing URL variable).
INVALID_TICKER400Empty/invalid {ticker} path segment.
MARKET_TICKER_REQUIRED400A Kalshi series was passed where an exact discovered market ticker is required.
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 book has been captured for the query — the market may not have opened for trading yet, may have expired, or may be outside coverage.
ENDPOINT_NOT_FOUND404Path matches no v3 route.
RATE_LIMIT_BURST429Per-second burst exceeded.
RATE_LIMIT_SUSTAINED429Per-minute sustained limit exceeded.
HISTORY_CONCURRENCY_LIMIT429Another snapshot or batch history request is still running for this account (details.scope = "account").
HISTORY_POOL_BUSY429The shared archive-query pool is saturated server-side, not your account's limit (details.scope = "shared_pool") — retry with backoff.
INTERNAL_ERROR500Unexpected server error — retry, then contact us with the request id.
AUTH_UNAVAILABLE503Key verification is temporarily unavailable — retry shortly.
UPSTREAM_UNAVAILABLE503A depth service behind the API did not answer (details.retryable = true). This is about us, not your ticker — retry with backoff rather than treating the market as missing.
BARS_BUSY503The bar service is at its concurrency ceiling right now, not an outage. Carries details.retry_after in seconds — back off and retry, the same request usually succeeds.

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.

The advertised rps/rpm values are general REST admission limits, not archive-delivery rates. Historical snapshots, batches containing history reads, and other heavyweight routes use separate account-level concurrency and delivery controls. These controls apply independently of the general token bucket and do not change the plan's history window.

Explorer, Quant, and Research currently accept one historical request in flight per account. Desk accepts 2 parallel historical snapshot requests per account through its dedicated Desk query pool. An overlap returns 429 HISTORY_CONCURRENCY_LIMIT with Retry-After: 2; retry only after an active request completes. General rps/rpm values must not be used to infer historical parallelism.

A historical read can also be rejected when your account is still inside its own allowance, because the shared archive-query pool is saturated. That case returns 429 HISTORY_POOL_BUSY with details.scope = "shared_pool" instead of "account". Branch on details.scope: on account, lower your parallelism; on shared_pool, lowering parallelism will not help — retry with backoff.

/v3/bars has its own concurrency ceiling, independent of both controls above. Past it a request is refused immediately with 503 BARS_BUSY and details.retry_after in seconds, rather than being queued — a fast refusal you can retry is more useful than a slow success you cannot budget for. Treat it exactly like a 429: back off and retry the same request. Bars also carry two limits that are not rate limits at all — batch size (402 SYMBOL_LIMIT_EXCEEDED) and per-request span (400 WINDOW_TOO_WIDE) — both fixed by splitting the request, not by waiting.

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.

Historical query pattern

Keep no more than your confirmed history concurrency active. Use bounded start_time andend_time, cursor pagination, and limit=1000; omit include_count and request the full ladder only when needed. Add interval when a downsampled series is sufficient. DepthFeed is a metered query-access product: bulk files, offline archive delivery, database replication, and systematic archive mirroring are not included.

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.
intervaldurSnapshots only. Downsample to one book per bucket (latest in each): 30s, 1m, 5m, 1h … up to 1d. Omit for full resolution.

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.

Downsampling with interval

By default the snapshot endpoints return every recorded book change — full event-driven resolution. When you only need a periodic sample (say one book per minute for a chart or a coarse backtest), add ?interval= and the server collapses each time bucket to a single snapshot: the most recent book in that bucket. It is far less data over the wire than fetching every tick and thinning client-side.

shell
# one snapshot per minute instead of the raw stored series
curl "https://api.depthfeed.com/v3/btc/markets/{id}/snapshots?interval=1m&include_orderbook=true" \
  -H "Authorization: Bearer df_your_key"

Accepts a duration: a bare number of seconds, or a value with a unit — 30s, 1m, 5m, 15m, 1h, 4h, 1d — from 1s to 1d. Buckets are aligned to the epoch, so 1m snaps to wall-clock minute boundaries. Cursor pagination and include_count apply to the downsampled rows (total_count becomes the number of buckets). Omit it, pass raw, or pass 0 for full resolution. An unrecognised value returns 400 INVALID_INTERVAL. Works on every snapshot endpoint — Polymarket, Kalshi, Limitless, and Binance.

Plans & access

Four flat plans. The plan field returned by /v3/whoami is the raw value in the right column.

PlanplanPriceGeneral rpsGeneral rpmHistorical parallelismUnified historyWallet IntelligenceCryptoSports RESTLive sports books
Explorerfree$0117 days1 saved · 3 auditsBTC sampleAll sports venuesREST only
Quant pro$29/mo251,000130 days5 saved · 10 auditsAll assets + venuesAll sports venues1
Researchscale$99/mo503,000190 days10 saved · 25 auditsAll assets + venuesAll sports venues2
Deskenterprise$249/mo1006,0002 parallelFull archive20 saved · 50 auditsAll; dedicated Desk historical query poolAll sports venues3

There are no Polymarket or Kalshi add-ons. Both sports venues use the same key, schema, rate limit, and history window; upgrading buys more history, throughput, and live-book concurrency.

The General rps/rpm columns describe request admission, not simultaneous archive scans. Historical query parallelism and delivery controls are separate. Desk historical reads use a dedicated Desk-only query pool isolated from regular-plan query traffic, without removing account-level controls. The pool is shared across Desk accounts and is not customer-owned or account-exclusive infrastructure.

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.
  • Sports — the /v3/sports/* REST surface is included on every plan; the same plan history window applies to both venues.
  • Historystart_time beyond your window → 403 HISTORY_LIMIT_EXCEEDED.
  • Price bars /v3/bars obeys the same history window as depth, and additionally caps symbols per request (3 / 8 / 16 / 32), over which it returns 402 SYMBOL_LIMIT_EXCEEDED.
  • Streaming — limits apply at subscribe time on /v3/stream; Quant and up stream every venue, Explorer streams Polymarket + Binance (BTC) — Kalshi and Limitless channels require a paid plan. Streaming is live depth; Kalshi HISTORY is on every plan, bounded by your window.

Health & identity

GET/v3/healthno auth

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

GET/v3/whoami

Your plan, rate limits, unified history window, coin access, and sports 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": { … } }
The settlement fields — *_price_*, winner, resolved_at, final_volume, and final_liquidity — are null until the market settles and the reference backfills (~12–24 h after settlement).
GET/v3/polymarket/marketspaid plan

The whole Polymarket venue, not just the crypto up/down markets. The routes above address markets by coin, so they can only reach markets that have one — elections, geopolitics, equities and FX have no coin and no URL there. This lists everything.

Filters: ?q= (slug search), ?type=eventfor everything that isn't an up/down ladder, plus ?coin=, ?event= and ?outcome=. Markets here are keyed by CLOB asset_id (token id); pass that to /v3/polymarket/{asset_id}/snapshots for depth history or /v3/polymarket/{asset_id}/ticks for the tape. Non-crypto markets carry an empty base_asset— they have no coin, and we don't invent one.

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. Add ?interval=1m (or 5m, 1h, …) to downsample to one book per bucket instead of the raw stored series — see Pagination & filters. The list response is an array with a pagination block:

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": { … } }
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. Unlike the list endpoint, data is a single object (the same row shape) with no pagination block; no match within ±2 s returns 404 SNAPSHOT_NOT_FOUND.

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], …] }
}, "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.

Tick data — every book change

The raw tape: every individual order-book change, not a sampled snapshot series. Where snapshots give you the book at points in time, ticks give you every change between them — what a market-making or execution-research workflow needs to ask where the book was the instant before a fill, or how long a level rested before it was taken.

Enterprise only. Other plans receive 402 TICKS_NOT_IN_PLAN.

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

Polymarket, sourced from the CLOB delta stream. Both outcome tokens are returned by default and tagged outcome; narrow with ?outcome=up or ?outcome=down.

History: approximately 45 days of continuous tick history. Older isolated days exist in the archive but are not contiguous, so treat 45 days as the dependable window.

Coverage: the up/down markets on the 5m, 15m and 24h windows, across all seven assets. The 1h and 4h up/down families are not currently in the tick tape — use snapshots for those.

json
{ "data": [{
  "id": "1780774485780", "time": "2026-06-06 19:34:45.780",
  "received_ms": "1780774485812", "outcome": "up",
  "token_id": "7194…", "side": "BUY", "price": 0.505, "size": 1240.0
}], "pagination": { … }, "meta": { … } }
GET/v3/kalshi/{ticker}/ticksenterprise

New — Kalshi tick capture began 2026-08-07. There is no Kalshi tick history before that date; the tape accumulates forward from it. Kalshi snapshots remain available over the full history window.

Coverage is a rolling subset, not every Kalshi market: the seven crypto assets, prioritised by nearest expiry, with a bounded number of markets taped concurrently. Contact us if you need a specific series covered.

Sourced from the authenticated orderbook_delta WebSocket. kind is snapshot for the rows that anchor a book (so any window you request starts from complete state) or delta for a change. delta is the signed size change and size is the resting size at that price after it. seqis Kalshi's own sequence number for the capture session — it increments by exactly one across the session, so you can verify a stretch of tape is complete rather than take our word for it. A run of snapshot rows marks a new session, where it restarts. Narrow to one side with ?side=yes or ?side=no.

json
{ "data": [{
  "id": "1780774485780", "time": "2026-06-06 19:34:45.780",
  "received_ms": "1780774485791", "seq": "418823",
  "kind": "delta", "side": "yes", "price": 0.62, "delta": -85.0, "size": 240.0
}], "pagination": { … }, "meta": { … } }

Pages hold up to 10000 rows (default 1000) and are cursor-paginated like every other list endpoint. Many ticks routinely share one millisecond, so cursors carry a position within a timestamp — paginating a busy market never splits or drops the rows sharing a boundary instant.start_time/end_timeand your plan's history window apply as usual.

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": { … } }
Binance doesn't deliver futures aggTrade over the websocket to our region (depth streams fine), so the futures 1s trade tape is polled from the REST aggTrades endpoint and aggregated to the same 1s OHLCV as spot. Spot and futures trades, and all books, are live.

Price bars — equities & crypto

OHLCV price bars for US-listed equities and ETFs, Binance spot pairs, and perpetual futures. One minute is the finest resolution; the coarser frames are rolled up from it.

GET/v3/bars

symbols is a comma-separated list. timeframe is one of 1Min, 5Min, 15Min, 1Hour or 1Day (default 1Min). start_time, end_time, limit and cursor behave exactly as they do on every other collection here.

venue is optional and selects the market outright: us-equities, binance (spot) or binance-futures (perpetuals). Leave it off and a bare symbol is resolved for you — equities first, then Binance spot. Name it and you get that market or nothing, which is what you want when a string could be two instruments.

limit is bars per symbol. A request without one is not paged at all, because the natural bars pull is a whole window and a default page size would quietly hand back a short series — which is indistinguishable from a gap in the data. When you do page, every series is cut at the same instant and pagination.next_cursor resumes exactly there: no overlap, no gap.

bash
curl -H "X-API-Key: $DEPTHFEED_KEY" \
  "https://api.depthfeed.com/v3/bars?symbols=AAPL,BTCUSDT&timeframe=1Hour&start_time=2026-08-01T00:00:00Z"
json
{ "data": {
  "timeframe": "1Hour",
  "license_class": "source_attributed_public_serving",
  "attribution": "Market data via QuarterTrace. Source-attributed; not a consolidated (SIP) feed.",
  "series": [{
    "symbol": "AAPL", "timeframe": "1Hour", "assetClass": "equity",
    "source": "yahoo_finance", "derived": true,
    "t": [1785974400000, 1785978000000],
    "open": [306.83, 307.04], "high": [307.9, 308.1],
    "low": [306.1, 306.55], "close": [307.04, 307.8],
    "volume": [1043200, 887400]
  }]
}, "meta": { … } }

How to spell a symbol

Equities and ETFs are their ordinary ticker: AAPL, SPY, BRK-B. A crypto pair is spelled the way the venue spells it, so it is BTCUSDT rather than BTC. A perpetual future takes a .P suffix — BTCUSDT.P, AAPLUSDT.P — because the spot pair and the perp are different instruments trading at different prices, and a flat symbol space that quietly picked one for you would be picking which market your strategy measured.

Three strings are both a US ETF and a Binance pair: BTCU, ETHU and WLDU. Each resolves to the equity, decided against the symbol universe rather than your date range — so the answer never changes when you move the window. The crypto pair is still reachable under its own venue spelling.

Derived frames

Equities are collected at 1Min and 1Day; crypto is collected at 1Min. Anything else is aggregated on read, and a series says which it is with derived. An aggregated bar takes the first bar's open and the last bar's close, the extremes of the high and low, and the sum of the volume, over UTC-aligned buckets.

Limits

Three separate bounds, and they fail with three different codes. Your plan's history window applies here as it does everywhere else — past it, 403 HISTORY_LIMIT_EXCEEDED. Symbols per request is 3 on Explorer, 8 on Quant, 16 on Research, 32 on Desk; over that, 402 SYMBOL_LIMIT_EXCEEDED. And a single request may only span as much time as the frame's row count allows — 45 days at 1Min, 180 at 5Min, a year at 15Min and 1Hour, ten years at 1Day — over which it returns 400 WINDOW_TOO_WIDE. Page with limit and cursor to walk a range wider than one request carries.

A fourth response is not a limit but a moment: 503 BARS_BUSY means the bar service is at its concurrency ceiling right now. It carries details.retry_after in seconds, like a rate-limit refusal, and the same request usually succeeds on the next attempt — treat it as back off and retry, not as an outage.

One window, one meaning. Bars are bounded by the same plan history window as order-book depth, refused the same way past it (403 HISTORY_LIMIT_EXCEEDED, with oldest_allowed in the details). The open-source Strategy Builder reads this exact endpoint with your own key, so a backtest there reaches back exactly as far as your plan does here.
This data is source-attributed and is not a consolidated (SIP) feed, and it is not exchange-affiliated, NBBO or a direct exchange redistribution feed. Every response carries the attribution string and each series names its own source; both have to travel with the data if you display it.

Kalshi

Kalshi's crypto contracts — full yes/no depth the reference doesn't carry. Paid plans only. 21 series for all 7 assets: 15-minute KX{ASSET}15M, threshold KX{ASSET}, and directional KX{ASSET}D. The threshold and directional series each run hourly, daily, and weekly markets concurrently — market_typecarries each market's actual window.

GET/v3/kalshi/markets

Filters: ?coin= (base asset), ?type= (window), and exact ?series= (for example KXBTC15M). For historical downloads, pass start_time and end_time here first and use the returned venue-native tickers. Do not generate Kalshi ticker ids from UTC timestamps; the venue's ticker clock convention changed historically.

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}/snapshots

The historical yes/no depth series for one exact discovered market ticker — same row shape, ordered by receive time, time-windowed + cursor-paginated (include_count opt-in, max 1000). The forward-captured Kalshi book history from a paced REST polling loop. Effective cadence varies with the active market set and API limits; it uses the same historical endpoint pattern as Polymarket and Limitless /snapshots, so Kalshi depth is backtestable too. Passing a series such as KXBTC15M, or an invented historical ticker, returns an explicit discovery error instead of a misleading empty page.

GET/v3/kalshi/{ticker}/candles

Hourly OHLC of the yes bid, the yes ask and the traded price, plus volume and open_interest. This is the only history that exists for most of the Kalshi catalog: Kalshi serves no historical order book, so a market that closed before our book capture began has no /snapshots and no /ticks — but it does have candles, reaching back up to a year.

Time-windowed and cursor-paginated like /snapshots(max 5000 rows), clamped to your plan's history window. ?interval= selects the candle period; only 1h is recorded, and any other value returns an explicit error naming what exists rather than an empty page. Candles are stored as recorded and never resampled, so a shorter period is never synthesised from a longer one.

price is null when nothing traded in the period — about 95% of them. The quote sides are always real, so a quiet hour still tells you where the book stood. Coverage is long-lived markets (open to close longer than 24 hours); short-dated crypto ladders are captured at full resolution by /snapshots and /ticks instead, and asking for their candles returns a 404 that says so.

json
{ "data": [{
  "ticker": "KXFEDDECISION-26JUL-H26", "series": "KXFEDDECISION",
  "market_type": "1w", "id": "1786168800000", "timestamp": "…",
  "period_minutes": 60,
  "yes_bid": { "open": 0.56, "high": 0.58, "low": 0.55, "close": 0.57 },
  "yes_ask": { "open": 0.64, "high": 0.65, "low": 0.62, "close": 0.63 },
  "price":   { "open": 0.60, "high": 0.61, "low": 0.59, "close": 0.61 },
  "volume": 240, "open_interest": 1180
}], "pagination": { … }, "meta": { … } }

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.

Binance prediction markets

Binance Wallet's Predict.fun-backed crypto prediction markets, normalized into a dedicated DepthFeed namespace. The initial catalogue covers BTC, ETH, and BNB up/down markets. Paid plans only.

GET/v3/binance-prediction/marketspaid plan

Filter with ?coin=btc, ?type=5m, or ?status=resolved. Results include the market/topic IDs, condition and token metadata, resolution, winning outcome, volume, liquidity, participant count, chain, and exchange contract.

GET/v3/binance-prediction/{market_id}/orderbook/latestpaid plan
GET/v3/binance-prediction/{market_id}/snapshotspaid plan

Latest and historical full L2 depth. The standardized BTC 15-minute lifecycle panel begins 26 June 2026; stable one-minute forward capture across tracked live markets begins 26 July. Earlier resting books cannot be reconstructed from settlement data.

GET/v3/binance-prediction/{market_id}/timeseries?metric=chancepaid plan
GET/v3/binance-prediction/{market_id}/timeseries?metric=pricepaid plan
GET/v3/binance-prediction/{market_id}/timeseries?metric=last_tradepaid plan
GET/v3/binance-prediction/{market_id}/tradespaid plan

Probability, displayed price, and last-trade samples are kept separately and begin on 17 July 2026. The trades route is reserved while historical fills are reconstructed from BNB Smart Chain logs and currently returns an empty historical dataset.

Deterministic 5-minute and 15-minute catalogue, resolution, volume, and liquidity history is backfilled from 5 May 2026. The equivalent /v3/predictfun/* paths remain compatible aliases. Customers use a DepthFeed key; no Binance key or wallet pairing is required.

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 Limitless cells fill in on a paid plan; the Kalshi cell is live depth, so it does too. Kalshi history is on every 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 dashboard 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" },
    { "id": "c", "method": "GET", "path": "/v3/bars?symbols=BTCUSDT&timeframe=1Hour" }
  ] }'

A contract's depth and the underlying's price bars in one round-trip is the pairing this exists for — /v3/bars is a sub-request like any other.

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.

Sports

Realtime sports prediction-market data — live order-book depth on Polymarket and Kalshi, alongside sportsbook odds, live scores, and an injury feed across the major leagues (NBA, NFL, MLB, NHL, soccer, tennis, UFC, F1 and more). Both venues are included together on every tier; plans change the shared history window, throughput, and live-book concurrency. Same{ data, meta, pagination } envelope, auth, and cursor pagination as the rest of the API.

Discovering a market. Walk the funnel: /v3/sports/leagues/v3/sports/markets?league=… → take a market's tokens[](each is one outcome's asset_id) → /v3/sports/books/{asset_id}/latest for the live book. Or start from /v3/sports/overview, which answers “what's trading now, on every venue” in one call.

GET/v3/sports/overview

Start here. The live cross-sport board — every tracked league with its in-progress and upcoming games, current prices, and best bid/ask per venue, in a single request.

GET/v3/sports/leagues

The leagues we track, each with its game and market counts — the top of the discovery funnel.

GET/v3/sports/markets

Sports markets across Polymarket and Kalshi. Filter with ?league= (e.g. nba, mlb, soccer); cursor-paginated like every list endpoint (?limit=, ?cursor=). Each market carries its condition_id, market_slug, outcomes, and tokens — the per-outcome asset_ids you hand to the book endpoint.

json
{ "data": [{
  "condition_id": "0xfb53…", "market_slug": "mls-nyc-laf-…-total-2pt5",
  "league": "soccer", "kind": "totals", "title": "New York City FC vs. Los Angeles FC",
  "outcomes": ["Over", "Under"],
  "tokens": ["45485059…", "38846626…"],   // one asset_id per outcome
  "game_start": "2026-11-05T00:30:00Z", "resolved": false
}], "pagination": { "next_cursor": "…", "has_more": true, "limit": 50 }, "meta": { … } }
GET/v3/sports/books/{asset_id}/latest

Newest recorded full order book for one outcome — the asset_idis a token from the market'stokens[]. Same [price, size] bid/ask ladder shape as the crypto book endpoints. For latency-sensitive live use, Quant and higher tiers can subscribe to the sports WebSocket channels below.

GET/v3/sports/books/{asset_id}/history

Historical full-ladder snapshots for the outcome. Page with ?limit= and ?cursor=; bound time with ?start_time= / ?end_time= (the shorter ?from= / ?to= aliases are also accepted). Add ?interval=1m to return the latest snapshot in each epoch-aligned bucket; supported units are s, m, h, and d, from one second through one day. Omit it, or useraw / tick, for every captured change. Sports depth is forward-captured: the Polymarket archive starts 14 April 2026 and the Kalshi archive starts 12 June 2026.

Games and resolved market context

GET/v3/sports/{league}/games

Live games and scores for a league (e.g. nba, nfl, soccer/eng.1), each with its linked Polymarket & Kalshi markets.

GET/v3/sports/games/{game_slug}

The joined game document: schedule and final score, linked prediction markets and settlements, sportsbook lines, and injury context. Use the companion routes below when you need an individual high-volume layer.

GET/v3/sports/games/{game_slug}/markets
GET/v3/sports/games/{game_slug}/lines
GET/v3/sports/games/{game_slug}/box
GET/v3/sports/games/{game_slug}/plays
GET/v3/sports/games/{game_slug}/winprob

Game markets, line movement, box scores, play-by-play, and per-play win probability.

GET/v3/sports/markets/{condition_id}

One normalized market with venue identifiers, outcomes, token IDs, game join, and settlement state.

Players, props, and injuries

GET/v3/sports/{league}/players

Search the league athlete directory and retrieve stable athlete IDs used by player-prop joins.

GET/v3/sports/players/{athlete_id}
GET/v3/sports/players/{athlete_id}/injuries
GET/v3/sports/players/{athlete_id}/box
GET/v3/sports/players/{athlete_id}/markets

Player profile, injury change-log, box-score game log, and entity-resolved prediction-market props.

GET/v3/sports/{league}/injuries

The live injury feed per league — the pre-game status changes that move the market.

Sportsbook odds

GET/v3/sports/odds/lines

Live moneyline, spread, total, and prop lines across books, normalized for comparison with the market price.

GET/v3/sports/odds/books

The active sportsbook catalog and source freshness.

GET/v3/sports/{league}/odds-history

Historical sportsbook lines, including NFL history to 1999 and European soccer to the 2000/01 season. Bound NFL requests with season, team, from, or to. Soccer requests require a football-data season code (for example 2324) and/or team.

GET/v3/sports/odds/stream

Server-Sent Events for changing sportsbook lines. Send Accept: text/event-stream; events pass through as they arrive and the connection remains open. Sports order books use the main DepthFeed WebSocket below.

Injury webhooks

POST/v3/sports/webhookspaid plan

Register an HTTPS receiver with league and injury-status filters. The signing secret is shown once. Deliveries include X-DepthFeed-Event and X-DepthFeed-Signature; verify the HMAC-SHA256 signature over the exact raw request body before parsing JSON.

shell
curl -X POST "https://api.depthfeed.com/v3/sports/webhooks" \
  -H "Authorization: Bearer df_your_key" -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/depthfeed","leagues":["nba","nfl"],"statuses":["out","questionable"]}'
GET/v3/sports/webhookspaid plan
POST/v3/sports/webhooks/{sub_id}/testpaid plan
DELETE/v3/sports/webhooks/{sub_id}paid plan

List subscriptions and delivery health, send a signed test event, or revoke a subscription.

shell
# discover a market, then pull its live book
curl "https://api.depthfeed.com/v3/sports/markets?league=mlb&limit=1" -H "Authorization: Bearer df_your_key"
# take tokens[0] from the response, then:
curl "https://api.depthfeed.com/v3/sports/books/<asset_id>/latest" -H "Authorization: Bearer df_your_key"

Live stream (WebSocket)

GET/v3/stream

A WebSocket that pushes live order books and prices as they change. Frame payloads are the same JSON objects the REST snapshot endpoints return, so code written against REST transfers directly. Authenticate at connect time with the X-API-Key header, Authorization: Bearer, or — for browsers, which cannot set WebSocket headers — ?api_key=df_….

shell
# wscat -c "wss://api.depthfeed.com/v3/stream?api_key=df_your_key"
> {"op":"subscribe","channels":["book:polymarket:btc:2510645","price:btc"]}
< {"op":"subscribed","channels":[…],"active":2}
< {"channel":"price:btc","data":{"coin":"btc","price":63560.1,…},"ts":1781300671083}
< {"channel":"book:polymarket:btc:2510645","data":{…same shape as REST snapshot…},"ts":…}

Channels

ChannelPayloadNotes
book:polymarket:<coin>:<market_id>REST snapshot object (with orderbooks)Tick-level via our CLOB websocket tap
book:kalshi:<coin>:<ticker>Same shape as /v3/kalshi/…/snapshots rowsUpdates from the paced Kalshi polling loop
book:limitless:<coin>:<slug>Same shape as /v3/limitless/…/snapshots rowsPush-based (socket.io tap)
book:binance:<coin>:<spot|futures>Same shape as /v3/…/latest100ms depth feed
book:sports:polymarket:<token_id>Full sports ladderQuant+: first-party, event-driven CLOB stream
book:sports:kalshi:<ticker>Full sports ladderQuant+: best-effort Kalshi order-book delta websocket, relay fallback; change-only frames
price:<coin>{ coin, price, source, venue, id, time }Binance underlying, ~4 ticks/s max

Ops: subscribe · unsubscribe (each takes {"channels":[…]}, applied atomically) and pingpong. A trailing * market segment (e.g. book:kalshi:btc:*) subscribes a whole venue/coin — Desk plan only. Op-level problems (bad channel, over your subscription cap) come back as {"op":"error","code":…} frames and never drop the connection.

Sports channels use concrete identifiers and do not support wildcards. Quant, Research, and Desk allow one, two, and three concurrent sports books respectively. Every sports frame includes the complete ladder, venue, asset_id, exchange/receive timestamps, full_ladder: true, and adelivery field naming the source of each frame: polymarket_clob_ws,kalshi_orderbook_delta_ws, or scoretape_relaywhen the sports relay is serving. Kalshi sports books are driven by the authenticated Kalshi order-book delta websocket, with the relay as fallback, so frame timing follows exchange activity rather than a fixed polling interval. Read freshness from each observation's timestamps rather than assuming a refresh rate.

shell
> {"op":"subscribe","channels":[
    "book:sports:polymarket:<token_id>",
    "book:sports:kalshi:<market_ticker>"
  ]}
< {"op":"subscribed","channels":[…],"active":2}

Per-plan limits

PlanConnectionsSubscriptionsChannels
Explorer11BTC — Polymarket + Binance books + price
Quant15Every venue, all coins; 1 sports book
Research225Every venue; 2 sports books
Desk5100Every venue + wildcards; 3 concurrent sports books

Close codes & reconnecting

CodeMeaning
4001Authentication failed (missing / unknown / revoked key)
4003Plan connection limit reached
4029Op flooding — slow down
1013Key verification temporarily unavailable — retry shortly

The server heartbeats with protocol pings every 25s. Deploys and failovers drop connections by design — treat disconnect → reconnect with exponential backoff (250ms doubling to ~5s) and re-send your subscriptions as the normal lifecycle, not an error path.

Freshness, measured

Median capture→client delivery measured from an external client through our edge: Polymarket ≈10ms (p95 ≈30ms), Binance and Limitless sub-100ms, underlying price ≈110ms. Kalshi follows its REST capture cadence, ≈1s median. Numbers come from deploy/stream_freshness.py runs against production — we publish what we measure, nothing rounder.

Streamed messages are metered into your usage like REST responses. Subscriptions are limited per plan, and quotas are per user across all connections. A scoped, automatically expiring 7-day Desk evaluation is available on request for production-latency validation.

Wallet Intelligence

Wallet Intelligence is a read-only research workspace for public Polymarket wallets. Save a watchlist, discover traders from Polymarket's public leaderboard, and review public positions, recent activity, realized samples, and portfolio-level metrics in one place. It never connects a wallet, signs a transaction, or places a real order.

Next-book execution audit

For each eligible public trade, DepthFeed finds the first Polymarket book it recorded after the activity timestamp, walks the displayed ladder for the same number of shares, and reports the replay price, fillable fraction, observed delay, and signed price difference versus the public trade. The lookup window is 90 seconds. A missing match is shown as missing; it is never estimated.

Grades are mechanical: Depth remainedmeans at least 95% of the public size was displayed within 1¢ of the trader's price; Price moved means at least 95% remained but at a difference above 1¢; Size unavailable means less than 95% of the public size was displayed.

Forward paper copy

Start a paper mirror from any inspected wallet. Only public trades observed after the mirror is created are eligible: DepthFeed deduplicates each fill, applies the configured copy ratio and per-trade cap, then fills it at the next available live quote in the normal Paper Trading ledger. Cash, positions, P&L, the equity curve, circuit breaker, and settlement history appear under Paper Trading.

An optional HTTPS callback receives each successful copied fill. Its signing secret is shown once. Verify HMAC-SHA256(timestamp.body) against x-depthfeed-signature; the timestamp is sent in x-depthfeed-timestamp. Local, private-network, credential-bearing, non-HTTPS, and redirect destinations are rejected.

PlanSaved walletsAudited trades per refreshPublic activity rows
Explorer1330
Quant510100
Research1025250
Desk2050500

Interpretation limits

  • A public address may be only one wallet in a trader's portfolio and does not prove identity or ownership.
  • Public activity timestamps are not private order timestamps. The audit measures what DepthFeed observed after publication, not the trader's original latency.
  • Only trades in DepthFeed's recorded Polymarket coverage can be matched. Unsupported or uncaptured markets are shown without an audit.
  • The replay consumes displayed size mathematically; it cannot model hidden liquidity, queue priority, cancellation races, fees, or the market impact of a real follower order.
  • Leaderboard rank, P&L, positions, and activity come from Polymarket's public data services and may be delayed, revised, incomplete, or temporarily unavailable.
  • Wallet Intelligence and its paper mirror are research tooling—not live copy trading, investment advice, a recommendation, or a guarantee of execution.
Only watchlist addresses and user-authored labels are stored by DepthFeed. Public wallet data is fetched on demand and briefly cached; execution audits are computed against our recorded order books at request time.

Wallet research guides

Start with the Polymarket wallet tracker guide, then compare wallet analytics, leaderboard research, and copy-trading execution analysis.

Backtest Lab

The Backtest Lab (in your dashboard) backtests entry/exit strategies on resolved native Polymarket, directional Kalshi, and Limitless crypto markets — settlement-anchored, so every simulated trade has a known outcome. It runs on the same order-book history the API serves, within your plan's history window.

Venues and sampling

Polymarket supports 5m, 15m, 1h, 4h and 24h windows. Limitless supports captured 5m and 15m up/down contracts. Kalshi supports the captured directional 15m, 1h and 24h series; range buckets are excluded because YES does not mean UP. Kalshi defaults to one representative directional contract per event so a ladder of correlated strikes is not counted as independent evidence. Advanced users can inspect all strikes with an explicit correlation warning.

Strategies

Five built-in presets (late-favorite, spot-leads, dip-reversion, level-cross, cheap-lotto) plus a custom JavaScript editor: your script plans entries over each market's price path and runs sandboxed in a web worker — no network, hard time budget, stored locally in your browser and exportable as JSON.

Fill models

ModelEntry price
MidThe snapshot mid — fastest, most optimistic.
Mid + slippageMid shifted by a fixed slippage you set.
Book-depth VWAPWalks the real ask ladder level by level. Missing or one-sided depth is unfillable; it never falls back to a modeled midpoint.

Fees are included in sizing and net P&L. Polymarket and Limitless use an editable flat per-share assumption; Kalshi uses its non-linear expected-earnings formula with an editable rate and cent rounding per order.

Portable exports

Export the complete trade log as CSV, the strategy and execution assumptions as portable JSON, or a reproducible run bundle containing the exact tested sample, statistics, risk report, equity curve and trades. Exports never embed raw historical paths or order-book archives.

Position sizing

How much each trade stakes — a flat amount, or a fraction of a compounding bankroll:

SchemeStake per trade
FixedA flat dollar amount — simple, but the only scheme that can trade a bankroll to zero.
FractionalA constant fraction of your live bankroll (fixed-fractional). It compounds and can't reach zero from sizing alone.
KellyGrowth-optimal f* = (q − p)/(1 − p) for a $1 binary at price p, scaled by a fractional-Kelly multiplier (half-Kelly = the standard hedge) and capped per trade. The win probability q comes from a fixed edge, a fixed win probability, or the strategy's own trailing hit rate. No edge (f* ≤ 0) ⇒ the entry is skipped.

Risk of ruin

Every run reports a risk panel derived from its own trades: the optimal fraction your edge can support (Ralph Vince optimal-f), a recommended max (half of it), a Monte-Carlo risk of ruin (the chance the bankroll ever falls to ≤30% of its start), and an over-bettingwarning when you're staking past the growth-optimal point — where more risk buys less long-run growth. Deploying to paper auto-caps the live per-trade size at this safe fraction.

Results: trade log, win rate, ROI, Sharpe, max drawdown, the equity curve, and the risk-of-ruin panel. Paid plans can save up to 50 frozen runs (they re-render identically later, no data re-reads), and the built-in AI assistant helps write and debug strategies and interpret sizing and risk within a plan-metered allowance.

When a backtested rule looks good, deploy it to Paper Trading to forward-test it on live markets with virtual cash.

Paper trading

Paper Trading runs strategies forward on livemarkets with virtual cash. Fills, marks and settlement are all priced from DepthFeed's live order books — buys at the ask, sells at the bid, positions settle to $0/$1 automatically when the market resolves. Each strategy gets its own equity curve, positions view, and full trade log.

Three ways to trade

ModeHow it works
WebhookYour own system (bot, cron, notebook) POSTs buy/sell signals to a per-strategy token URL. Your logic stays yours — we do execution, accounting and charts.
Lab ruleDeploy a Polymarket or Kalshi Backtest Lab preset (time-window entry, level cross, dip reversion) with venue-native sides, take-profit/stop-loss, a max-open cap, and position sizing (fixed / fractional / Kelly); it's evaluated server-side against live quotes (~90s cadence).
Wallet copyMirror new public fills from an inspected Polymarket wallet with a copy ratio and per-trade cap. Results use the same paper ledger and may optionally emit signed HTTPS fill webhooks.

Signal webhook

POST/paper/hook/{token}no auth

The token (shown once at create/rotate) is the auth. Fills always price from the live book at signal time — a payload can never set its own price. Markets are identified by venue + market: a Kalshi ticker, or a Polymarket CLOB token id. You can trade any market on the venue, not just the ones DepthFeed records — if we don't have it locally, the fill is priced from a live read of the venue's own order book.

shell
curl -X POST 'https://api.depthfeed.com/paper/hook/pt_your_token' \
  -H 'content-type: application/json' \
  -d '{
    "action": "buy",              # buy | sell | close
    "venue": "polymarket",        # polymarket | kalshi
    "market": "1234567890...",    # CLOB token id / Kalshi ticker
    "side": "yes",                # kalshi: yes|no (polymarket tokens are one-sided)
    "usd": 100,                   # size by dollars… or "shares": 150
    "client_id": "sig-0042"       # optional idempotency key (safe retries)
  }'
json
{ "data": { "filled": true, "action": "buy", "price": 0.62,
            "shares": 161.29, "cost_cents": 10000, "cash_cents": 990000,
            "position_id": "…" } }
ErrorMeaning
NO_FRESH_QUOTENo live book for that market — a bad id, or the market has closed / gone one-sided at 0/1 (settlement closes positions automatically).
INSUFFICIENT_CASHThe order exceeds the strategy's paper cash.
NO_POSITIONsell/close with no open lot on that market/side.
RATE_LIMITEDOver 120 signals/min for the strategy.
PAUSEDThe strategy is paused in the dashboard.

Circuit breaker

Each strategy has a trailing max-drawdown stop (default 60%, editable; set to 0 to disable). If live equity ever falls that far below its high-water mark, the strategy is automatically flattened at current marks and halted — a hard stop against a strategy trading itself toward zero. Resume it any time; resuming resets the high-water mark to your current equity.

Limits

Concurrent strategies per plan: Explorer 1 · Quant 5 · Research 15 · Desk 40. Up to 100 open positions per strategy; starting balance $100–$1,000,000 (default $10,000). Marks refresh on a ~90-second cadence — this is a forward-testing tracker, not an execution venue.

Paper results use real displayed liquidity for pricing but don't consume it — live fills of size may see more slippage than the paper fill at top-of-book. Markets we don't record locally are priced from a live venue read and won't re-settle from our resolution data — they close at their last mark after a few days if the venue doesn't resolve them through a path we track.

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, paced full-depth polling15m · 1h · 24h · 1wYes/no book, up to 100 levels/side, 7 assets; effective cadence varies with market load and API limits.
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 & schema

Timestamps are epoch-millis. exch_ts_ms = exchange time, recv_ts_ms = our receive time (the difference is capture latency). These are the fields every order-book snapshot returns over the API.

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

History & coverage

Every plan window is served from the live store plus the cold archive, so the full window is always available. Desk has no rolling history cap: it can query every recorded snapshot from the applicable series floor through the live feed. The floors differ because contracts and licensed capture did not begin on the same day for every asset/window combination.

Polymarket full-depth archive floors (UTC)

Assets5m15m1h4h24h
BTC2026-02-122026-01-012026-01-012026-01-012026-01-01
ETH, SOL, XRP2026-02-182026-01-012026-01-012026-01-012026-01-01
BNB2026-03-132026-03-132026-03-062026-03-132026-03-07
DOGE2026-03-132026-03-132026-03-112026-03-132026-03-11
HYPE2026-03-132026-03-132026-03-062026-03-132026-03-07

These are the earliest days with recorded full ladders, not a claim that every asset/window existed before its floor. A snapshot returns the complete recorded ladder; it can legitimately contain no levels when the market itself had no resting orders at that instant.

Kalshi archive floors (UTC)

Kalshi floors are separate from the Polymarket ones above and are later. The earliest recorded Kalshi ladder of any series is 2026-01-08. For the 15-minute crypto series:

SeriesEarliest recorded ladder
KXBTC15M, KXETH15M2026-01-08
KXSOL15M2026-01-09
KXXRP15M2026-02-11
KXBNB15M, KXDOGE15M2026-03-07
KXHYPE15M2026-03-18

Hourly, daily and weekly Kalshi series carry their own per-series floors, and BTC, ETH, XRP and DOGE begin on 2026-01-08 there too. Rather than publish a floor per series, treat discovery as authoritative: /v3/kalshi/markets?series=KXBTC15M&start_time=…&end_time=… lists exactly the tickers whose depth is retrievable in that range, and Kalshi ticker ids are venue-defined, so they must be discovered rather than constructed from UTC timestamps.

Kalshi capture cadence and depth are not constant across the archive. Current capture polls the full ladder at up to 100 levels per side every few seconds; earlier periods are sparser, and some spring 2026 days carry only a handful of observations per contract. Snapshot rows are stored as recorded, so a research pull spanning the archive should resample onto a fixed grid and carry an observation count per bucket rather than assume a constant sampling interval.

Order-book depth is captured live and cannot be recreated after the fact. Archive access is delivered through the documented metered query surfaces; DepthFeed does not provide customer-requested reconstruction or bulk archive delivery.

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

AI agents & LLMs

DepthFeed is built to be driven by agents: plain JSON over HTTPS, one Bearer key, stable error codes, and no SDK required. A machine-readable product summary lives at /llms.txt.

As a function-calling / MCP-style tool

Wrap any endpoint as a tool — the API is self-describing enough that one generic definition covers most workflows:

json
{
  "name": "depthfeed_get",
  "description": "Query DepthFeed prediction-market order-book data. Paths: /v3/{coin}/markets, /v3/{coin}/markets/{id}/snapshots, /v3/kalshi/markets, /v3/kalshi/{ticker}/snapshots, /v3/kalshi/{ticker}/candles, /v3/limitless/{slug}/snapshots, /v3/{coin}/{spot|futures}/latest. Coins: btc eth sol xrp doge bnb hype.",
  "parameters": {
    "type": "object",
    "properties": { "path": { "type": "string", "description": "GET path under /v3, with query params" } },
    "required": ["path"]
  }
}

The tool body is a single authenticated GET:

shell
curl "https://api.depthfeed.com{path}" -H "Authorization: Bearer df_your_key"

Live data in agents

The WebSocket stream uses three JSON ops (subscribe / unsubscribe / ping) and authenticates with ?api_key=where headers can't be set — trivial to drive from any runtime. Frames carry the same JSON objects as REST, so an agent's parsing code is identical for history and live.

Tip for assistants: /v3/whoamiechoes the key's plan, rate limits, and history window — call it first and self-configure instead of guessing limits.

Status & changelog

What's live, newest first:

  • Live streaming. /v3/stream WebSocket — order-book and price channels on every venue, ~10ms median Polymarket delivery, plan-gated subscriptions.
  • Limitless venue. Markets, latest orderbook, and historical depth snapshots — with 5m + 15m captured live over the order-book websocket.
  • Kalshi expanded. 21 series for all 7 assets (15-minute, threshold, and directional — the latter two running hourly/daily/weekly concurrently); 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 backfill request? Get in touch.

Start backtesting Polymarket & Kalshi on real depth.

Free to start, no card. Upgrade when your strategy is ready for the full book.

Start free