File schemas

Two container formats, and two venues that do not share a field list. Everything below is a column or key you will actually find in a file, listed per dataset.

What outcometick is: The Chainlink settlement feeds and order books of Polymarket and Predict.fun crypto Up/Down markets, plus Polymarket’s trade prints — collected continuously, checksummed, and downloadable by API. 155 complete UTC days archived since 2026-06-08 (Predict.fun from 2026-06-13). Polymarket 7 assets, Predict.fun 3 assets. Subscriptions from $10/month for one venue.

Pricing · Free sample (Polymarket) · Free sample (Predict.fun) · Backtest a strategy

The two formats

Settlement feeds and klines are gzipped CSV with a header row. Market event streams are gzipped JSONL — one JSON object per line, in the order the frames arrived. Nothing is re-sorted on the way into the archive. Every file also comes as Parquet (format=parquet on /v1/files): the same rows and columns, already typed, with the nested fields kept as nested columns.

Settlement feeds — CSV (Polymarket side)

The same five columns for prices, twap30s and twap60s. This is the only place in the archive where three separate timestamps travel together (through 2026-10-19 UTC); server_ts_ms is empty from 2026-10-20 (UTC) on.

feed_ts_msEvent time: the timestamp upstream put on the report (UTC ms)
valuePrice as a float, for convenience
full_accuracy_valueWhenever the relay publishes it, the same price as a fixed-point integer string (1e18). Use this to recompute outcomes, not value
server_ts_msWhen the upstream server sent it (UTC ms); empty from 2026-10-20 (UTC) on
recv_msWhen we received it (UTC ms). All three are kept apart, never merged — so you can measure the capture path

Market events — the JSONL envelope (Polymarket side)

Every line in book, price_change, best_bid_ask and last_trade_price carries the same six top-level keys. Note there are TWO timestamps here, not three, and the event one is not called feed_ts_ms.

slugThe market this frame belongs to
asset_idWhich outcome token the frame is about. Null on price_change, because one of those lines carries the changes for both sides at once
event_typeSame name as the dataset
event_ts_msUpstream event time (UTC ms)
recv_msWhen we received it (UTC ms)
payloadThe upstream message, kept as it arrived

What is inside payload, per dataset

bookbids and asks, each an array of {price, size} objects, plus hash, market, asset_id and timestamp. One line is a snapshot of ONE outcome token — which one is the envelope asset_id — and carries both of its ladders. An empty array means that ladder holds no orders, not that it went unreported
price_changeprice_changes, an array whose items each carry hash, side, size, price, asset_id, best_ask and best_bid. Both outcome tokens appear in the same line, which is why the envelope asset_id is null
best_bid_askbest_bid, best_ask and spread, for one asset_id. Prices only — there are no sizes on this stream, so depth still comes from book or price_change
last_trade_priceside, size, price, fee_rate_bps and transaction_hash, for one asset_id

Rebuilding a ladder from these: book and price_change are captured at a cadence, not frame by frame — snapshots are kept per outcome token and deltas per market, so frames that arrived between two kept ones are not in the archive. Each price_change item sets a level to an absolute size (size 0 removes it), so a removal that fell between kept frames leaves that level in a rebuilt ladder until the next book line for that token replaces the whole ladder. To rebuild: start each token from its latest book line, apply price_change items on top, and drop any bid above or ask below the newest best bid and ask — from best_bid_ask, which is not throttled, on the days the archive has it, otherwise from the best_bid and best_ask carried on the price_change item itself. That removes every level the best prices have moved past. It cannot restore what the dropped frames added or resized: until the next snapshot a level can be missing or carry an old size, at the top as well as further back, because best_bid_ask carries prices only, never sizes. For the latest best bid and ask themselves, read best_bid_ask. The SDK applies this exact rule: OrderBook, in outcometick/data for Node (npm i outcometick) and outcometick.data for Python (pip install outcometick). Feed it the rows of the book, price_change and best_bid_ask files merged by recv_ms, then read ladder() per outcome token.

markets — one line per market

Metadata, the strike, and the settlement once it exists. The raw key holds the upstream market object unchanged, which is where the settlement configuration lives.

slug / asset / interval_secIdentity and duration. interval_sec is seconds
condition_id / token_idsOn-chain identifiers. token_ids has two entries and the FIRST is Up — established by measurement, not by assumption. For Polymarket Protocol V2 markets (new markets from about 2026-11-02, raw.version "v2") token_ids are V2 position ids, the ids the CLOB trades them under
start_sec / end_secMarket window, in SECONDS — not milliseconds like the event streams
resolved / outcome_pricesSettlement. ["1","0"] means Up won, ["0","1"] means Down won. Only a complete binary pair is meaningful
strike_valueThe price to beat, as a fixed-point integer string (1e18). Null when we cannot establish it honestly — never guessed from a nearby tick
rawThe upstream market object as received, including raw.cryptoMarketConfig — see how a market settles

klines — CSV

Derived from the settlement feed, so these are OHLC and there is no volume column. ticks counts settlement updates in the bucket; it is not traded size.

ts_msBucket start (UTC ms)
open / high / low / closePrices within the bucket
ticksHow many settlement updates fell in the bucket — an update count, not volume

Predict.fun does not share the schema above

Different column names, a different ladder shape, and different time units. If you subscribe to both platforms, treat them as two formats rather than one with extra fields.

prices (CSV)price_feed_id, symbol, provider, publish_time, server_ts, price, recv_ms. publish_time and server_ts are in SECONDS while recv_ms is milliseconds, and the price column is called price rather than value
orderbook (JSONL)market_id, category_slug, update_ts_ms, recv_ms, payload. Inside payload, bids and asks are arrays of [price, size] PAIRS — not {price, size} objects as on the Polymarket side — alongside version, orderCount, lastOrderSettled and settlementsPending
markets (JSONL)category_slug, asset, interval_label, market_id, price_feed_id, price_feed_symbol, price_feed_provider, condition_id, start_sec, end_sec, start_price, end_price and status. Prices here are ordinary decimal strings, not 1e18 fixed point

One thing worth knowing before you write a settlement check against this: status reports what upstream last told us, and a market stops being re-read once its end_price arrives. end_price is therefore the field that tells you a Predict.fun market has settled. A market still open when its day ships (the daily ones, and hourly ones that start just after midnight) has an empty end_price in that day's file; it also appears in the next day's file, which carries the settled values, so read it from there.

Unified markets (Parquet, both platforms)

dataset=markets_unified with format=parquet: one row per market and the same columns for Polymarket and Predict.fun, so the two read in one query. Each is built from the markets file of the same day and gated like it.

venue, market_id, condition_id, slugpolymarket or predict. market_id is the Polymarket condition_id or the Predict.fun market_id; slug is the Polymarket slug or the Predict.fun category_slug
asset, intervalBTC, ETH …; 5m, 15m, 1h, 1d
open_ts_ms, close_ts_msStart and end in milliseconds (start_sec and end_sec × 1000)
open_priceDECIMAL(38,18). The price the market is judged against: the Polymarket strike_value divided by 1e18, or the Predict.fun start_price
settle_priceDECIMAL(38,18). The Predict.fun end_price. Empty for Polymarket, whose settling value is in the stream settle_feed names
outcomeUP, DOWN or TIE (Predict.fun settles an unchanged price 50:50). Empty while a market is unsettled in that file, or when its result could not be read
settle_provider, settle_feedPolymarket: chainlink and the stream it settles on (twap60s, twap30s or prices). Predict.fun: price_feed_provider in lower case and price_feed_symbol
token_up, token_downThe Polymarket token ids, as text. Empty for Predict.fun

A market listed twice in one file keeps its last row. A Predict.fun market still open when its day ships settles in the next day's file, so when the same market_id turns up in two days, take the row that has an outcome.

Working with one of these in particular: Polymarket historical data · Polymarket order book data · Chainlink settlement data · Polymarket settlement audit · Predict.fun settlement audit · Predict.fun historical data · Predict.fun order book data · Compared with other providers