Resolved Markets logoResolved Markets logo
Abstract blue gradient background with a soft light streakAbstract blue gradient background with a soft light streak
Data

Sequence numbers, honestly

Every snapshot carries a sequence_number, and it is widely misread as a completeness check. It counts book events applied, not rows stored, so jumps are the normal case. Here is what it actually measures and what to use when you need the gap filled.

9 min read · Updated Aug 20, 2026

  • 98.4%Pairs that jump
  • 109 msMedian row gap
  • ~28Events per gap
  • 50 msCapture throttle

A backtest is only as trustworthy as the series underneath it, so it is tempting to reach for the sequence number as a completeness check: walk the series, and where the numbers jump, you found a hole. That method is wrong, and the data says so bluntly. On BTC 5m snapshots, 98.4% of adjacent stored rows jump by more than one. The counter is not measuring what most people think it is.

Correction, August 2026

This post used to say the opposite

An earlier version of this article told you that a jump in sequence_number flags a dropped event, and that a clean consecutive run proves a window is complete. Measurement disproved it: applied against real data, that rule would quarantine almost every window we store. The article has been rewritten around what the counter genuinely measures, and the API reference, the OpenAPI schema and the MCP tool descriptions have been corrected to match.

Capture here is event-driven, a snapshot is produced when Polymarket emits a change to the book rather than on a fixed polling clock. That much of the original framing was right, and it does raise the obvious question: how do you know what you received? The mistake was assuming the sequence number answers it.

What the counter actually counts

The sequence number is ours, not Polymarket’s. Each token’s book carries a counter that increments every time we apply an event to it, whether that event is a full snapshot or an incremental delta. Storage is a separate decision made afterwards: we keep at most one row per 50ms per token, and we drop rows whose top of book is unchanged. The throttle runs after the event has already been applied, so a dropped row still advances the counter. That single ordering detail is the whole misunderstanding.

Events, not rows

It counts what the collector applied to the book, not what survived the capture throttle, so the gap between two stored rows is expected sampling rather than loss.

  • Increments on every applied event
  • Throttle runs after apply
  • A jump is the norm, not an alarm

It resets

When a token’s book is dropped and re-subscribed, the counter starts again at 1. Window rollovers and connection replacements both do this routinely.

  • Restarts at 1
  • Every window rollover
  • Not monotonic across a lifetime

Backfilled rows are zero

Rows reconstructed from a historical source carry a constant 0. The counter only means something on rows we captured live.

  • Constant 0, not a counter
  • Check before differencing
  • Live rows only

Why a jump is the normal case

Measured across a week of BTC 5m markets, the median pair of consecutive stored rows sits 109ms apart and spans roughly 28 applied book events. About a quarter of all pairs are more than 150ms apart with ten or more events inside them. None of that is missing data in any meaningful sense, it is the sampling policy working exactly as designed: the book moved many times, and we recorded the state at the end of each 50ms slice rather than every intermediate frame.

The extreme case

One 239 ms window, 341 events, zero rows

In the same dataset, a single 239ms stretch carried 341 applied book events while the price ran from 0.905 to 0.190, and not one row was stored inside it. A completeness check would have flagged that window. What it would not have told you is the far more useful fact: the market traded through the entire move while your series was between samples.

What the counter is genuinely good for

Read correctly, it answers a better question than the one it was being asked. The delta between two consecutive rows tells you how much book activity your sample skipped, which turns an unknown into a measurable quantity. That is the honest version of the old quiet-versus-gap question, and unlike the completeness check, it actually works.

  1. 1Pull the snapshots for your market and window in ascending order, so the counter runs in capture order and each row can be compared to the one before it.
  2. 2Filter out any rows where sequence_number is 0, those are backfilled and carry no counter, and split the series wherever the value drops rather than rises, since a drop marks a re-subscribe rather than anything about the market.
  3. 3Take the delta between adjacent rows, that is the number of book events that happened between the two states you can actually see.
  4. 4Read a small delta across a long stretch of wall-clock time as a genuinely quiet book, and a large delta across a short stretch as an active book you are sampling coarsely. Those are very different regimes and this is the field that separates them.
  5. 5Where the delta is large and the move between rows is large, treat the interval as unresolved rather than as a straight line, and go to the trade tape for what happened inside it.

The fields this rests on

  • sequence_numberCount of applied book events, per token, on live rows
  • event_timestampWhen Polymarket emitted the change
  • capture_timestampWhen we processed it
  • order ascPull in capture order so deltas are meaningful

One caveat worth stating plainly: the counter is per token, so never compare it across the UP and DOWN legs of the same market. They are independent books with independent counters, and differencing across them produces a number that means nothing at all.

When you need what happened inside the gap

The snapshot series is a sample of the book. The trade tape is a different instrument: executions arrive on a separate WebSocket, unthrottled, carrying Polymarket’s own event timestamp. Where the snapshot series is silent between two rows, the tape frequently is not, which makes it the right tool for the question the sequence number cannot answer.

This matters more than it sounds. Across the same BTC 5m sample, of 1,569 downward crossings of a 0.80 level, 354 landed more than three cents below it in the next stored row, and 47 of those had a real execution inside the band the snapshots stepped over. A backtest reading snapshots alone would have filled those at prices the market never printed. Reading the tape alongside the book fixes it, and the correction only ever moves an exit earlier, never later.

Being honest about what you can and cannot claim

  • A sequence delta tells you how many events you skipped, it does not tell you what those events were or where the price travelled in between.
  • A clean consecutive run does not prove completeness, it usually just means an illiquid book that barely moved, which is a statement about the market rather than about your data.
  • Do not interpolate across a wide delta, that manufactures a path that never existed. Hold the last known state, skip the interval, or resolve it against the trade tape.
  • For actual capture-loss accounting, the throttle and dedup counters on the debug endpoint are the right instrument, because they distinguish deliberate skips from genuine drops. Sequence arithmetic cannot.
The counter was never lying to you. It answers a precise question, how much did the book move between the two states you can see, and that is a more useful thing to know than whether a series is nominally complete.

Resolve a window properly

Pull the snapshots and the trade tape for the same market and window from the API, then measure what your sample skipped instead of assuming it skipped nothing.

Frequently asked questions