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_ms | Event time: the timestamp upstream put on the report (UTC ms) |
| value | Price as a float, for convenience |
| full_accuracy_value | Whenever the relay publishes it, the same price as a fixed-point integer string (1e18). Use this to recompute outcomes, not value |
| server_ts_ms | When the upstream server sent it (UTC ms); empty from 2026-10-20 (UTC) on |
| recv_ms | When 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.
| slug | The market this frame belongs to |
| asset_id | Which outcome token the frame is about. Null on price_change, because one of those lines carries the changes for both sides at once |
| event_type | Same name as the dataset |
| event_ts_ms | Upstream event time (UTC ms) |
| recv_ms | When we received it (UTC ms) |
| payload | The upstream message, kept as it arrived |
What is inside payload, per dataset
| book | bids 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_change | price_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_ask | best_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_price | side, 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_sec | Identity and duration. interval_sec is seconds |
| condition_id / token_ids | On-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_sec | Market window, in SECONDS — not milliseconds like the event streams |
| resolved / outcome_prices | Settlement. ["1","0"] means Up won, ["0","1"] means Down won. Only a complete binary pair is meaningful |
| strike_value | The price to beat, as a fixed-point integer string (1e18). Null when we cannot establish it honestly — never guessed from a nearby tick |
| raw | The 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_ms | Bucket start (UTC ms) |
| open / high / low / close | Prices within the bucket |
| ticks | How 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, slug | polymarket 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, interval | BTC, ETH …; 5m, 15m, 1h, 1d |
| open_ts_ms, close_ts_ms | Start and end in milliseconds (start_sec and end_sec × 1000) |
| open_price | DECIMAL(38,18). The price the market is judged against: the Polymarket strike_value divided by 1e18, or the Predict.fun start_price |
| settle_price | DECIMAL(38,18). The Predict.fun end_price. Empty for Polymarket, whose settling value is in the stream settle_feed names |
| outcome | UP, 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_feed | Polymarket: 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_down | The 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