Guides / API Guide

Querying an Orderbook Snapshot at a Specific Timestamp

There is no "give me the book at exactly 14:32:07" endpoint. The historical orderbook endpoints are range queries, so a point-in-time lookup is a narrow range plus a choice about which side of the target to take. That choice matters more than it looks.

The query

Ask for a window bracketing your target and take the nearest snapshot:

# target 2025-01-20T00:00:00Z = 1737331200
GET /v1/historical/polymarket/orderbook
      ?market_id=<market_id>
      &granularity=1m
      &start_time=1737331140   # target - 60s
      &end_time=1737331260     # target + 60s
      &limit=10

Size the window to the granularity you requested. At 1m, ±60s reliably contains at least one snapshot; at 5m, use ±300s. A window narrower than the sampling interval can legitimately return zero rows.

Take the snapshot before, not the nearest

For anything resembling a backtest, use the last snapshot at or before the target timestamp, not whichever is closest. The nearest snapshot may be after your decision point, which means your strategy is reading a book that had not formed yet — a subtle look-ahead bias that quietly flatters results.

snapshots = res["snapshots"]
before = [s for s in snapshots if s["timestamp"] <= target]
book = max(before, key=lambda s: s["timestamp"]) if before else None

Don't do this in a loop

Resolving a thousand timestamps with a thousand ±60s requests is the fastest way to hit rate limits. Pull the whole range once, sort by timestamp, and resolve every target locally with a binary search. One request covering an hour beats sixty covering a minute each.

What you get back

Each snapshot carries timestamp, the depth arrays (bids/asks on Polymarket, yes_bids/no_bids on Kalshi), and the derived best_bid, best_ask, mid, and spread. If you only need top-of-book, the derived fields save you walking the arrays.

Related

Related guides

Data from Kalshi and Polymarket. Search live API docs Pricing