Kraken Spot wire format
How Kraken Spot messages become rows in our Parquet files. Each data type below shows a real message next to the rows we publish for it, with the source and the rule for every column. Hover a line or a column to see how they connect.
Checked against our pipeline
These messages were captured from Kraken Spot on 2026-10-05. Our test suite replays each one through the production collector parser and postprocessor, and fails if the published rows differ from the rows on this page. Files written before a change to the pipeline can differ; see Known Gaps & Corrections.
The collector uses Kraken's WebSocket v2 book (depth 1,000), trade and ticker channels. Kraken v2 sends prices and sizes as JSON numbers, such as 0.00005100. We keep each number's exact digits, trailing zeros included, so price and quantity hold the literal Kraken sent. Each entry of a message's data array is stored and published separately.
- Public WebSocket v2 (book, trade, ticker channels):
wss://ws.kraken.com/v2 - REST (trade gap backfill):
https://api.kraken.com/0/public/Trades
Exchange API reference: docs.kraken.com. Conventions shared by every exchange are on the wire formats overview.
Trades
One row per trade from the trade channel, plus trades recovered from REST when a trade_id gap is found.
- WebSocket trade
- REST GET /0/public/Trades
Trade
{"channel": "trade","type": "update","data": [{"symbol": "BTC/USD",→ symbol"side": "sell",→ is_buyer_maker"price": 85676.6,→ price"qty": 0.00001764,→ quantity"ord_type": "limit",→ order_type"trade_id": 110284812,→ trade_id"timestamp": "2026-10-05T19:18:02.218613Z"→ event_time · trade_time}]}
Published row
timestamp in milliseconds since the Unix epoch. Microseconds are truncated. Same value as trade_time.BTC/USD.timestamp in milliseconds since the Unix epoch. Microseconds are truncated. If the timestamp does not parse, our receive time in milliseconds is used.true when side is sell. Kraken's side is the taker's side.limit or market.Subscription trade snapshot
The collector subscribes with snapshot: true, so after every subscribe, including reconnects, Kraken resends the pair's 50 most recent trades. They fill trades missed while reconnecting. A trade_id already received or published in an earlier hour is dropped, and so is any trade timed more than 5 seconds before the hour being processed. This snapshot arrived 9.7 seconds after 20:00 UTC; its oldest trades, 4.7 and 2.6 seconds before the hour, are within that margin and are published in the 20:00 hour.
Abridged: Kraken sent 50 trades in this snapshot; the two oldest and the newest are shown. Each trade becomes its own row.
{"channel": "trade","type": "snapshot","data": [{"symbol": "BTC/USD",→ symbol"side": "sell",→ is_buyer_maker"price": 85706.1,→ price"qty": 0.00023200,→ quantity"ord_type": "limit",→ order_type"trade_id": 110288956,→ trade_id"timestamp": "2026-10-05T19:59:55.347091Z"→ event_time · trade_time},{"symbol": "BTC/USD",→ symbol"side": "buy",→ is_buyer_maker"price": 85706.0,→ price"qty": 0.00043755,→ quantity"ord_type": "limit",→ order_type"trade_id": 110288957,→ trade_id"timestamp": "2026-10-05T19:59:57.436061Z"→ event_time · trade_time},{"symbol": "BTC/USD",→ symbol"side": "sell",→ is_buyer_maker"price": 85723.2,→ price"qty": 0.00001322,→ quantity"ord_type": "limit",→ order_type"trade_id": 110289005,→ trade_id"timestamp": "2026-10-05T20:00:08.679839Z"→ event_time · trade_time}]}
Published rows · 3
timestamp in milliseconds since the Unix epoch. Microseconds are truncated. Same value as trade_time.BTC/USD.timestamp in milliseconds since the Unix epoch. Microseconds are truncated. If the timestamp does not parse, our receive time in milliseconds is used.true when side is sell. Kraken's side is the taker's side.limit or market.REST backfill of a trade ID gap
When a trade's trade_id skips ahead of the last one the collector received for the pair, the collector fetches the missing IDs from REST GET /0/public/Trades (from the time of the last received trade, up to 1,000 trades per request) and stores each missing trade as a WebSocket-shaped line. Trades that were received on the WebSocket are not stored again. In this example, trade 110285002 stands for a missing trade.
Abridged: Requested with count=3 to keep the example short; the collector requests count=1000.
{"error": [],"result": {"BTC/USD": [["85788.50000", "0.00043713", 1791227953.2866168, "s", "l", "", 110285002],→ event_time · trade_id · price · quantity · trade_time · is_buyer_maker · order_type["85788.50000", "0.00010000", 1791227953.2866168, "s", "l", "", 110285003],→ event_time · trade_id · price · quantity · trade_time · is_buyer_maker · order_type["85783.20000", "0.00043715", 1791227953.6063356, "s", "l", "", 110285004]→ event_time · trade_id · price · quantity · trade_time · is_buyer_maker · order_type],"last": "1791227953606335684"}}
Published row
trade_time.85788.50000) that WebSocket rows do not have.true when the side is s (taker sold).limit for l, market for m.Notes
trade_idis consecutive per pair; a jump that remains after the REST backfill is a real gap.- Rows recovered from REST keep the REST decimal strings, which can have trailing zeros (
85788.50000) where WebSocket rows have none (85788.5). Compare prices as numbers. - Before the 2026-09 data-integrity release, trades printed while the collector reconnected were not recovered, so older files can miss a few seconds of trades around reconnects.
Column types and descriptions: Trades schema.
Order Book
One row per price level from the book channel at depth 1,000: snapshots after each subscribe, then updates.
- WebSocket book
Book snapshot
The collector subscribes with depth: 1000. After every subscribe, including reconnects, Kraken sends the best 1,000 levels of each side in one snapshot message.
Abridged: Kraken sent 1000 bid and 1000 ask levels; the best three of each side are shown.
{"channel": "book","type": "snapshot",→ event_type"data": [{"symbol": "BTC/USD",→ symbol"bids": [→ side{→ side"price": 85676.6,→ side · price"qty": 0.09509427→ side · quantity},{→ side"price": 85675.9,→ side · price"qty": 0.04469152→ side · quantity},{→ side"price": 85675.8,→ side · price"qty": 0.17507841→ side · quantity}],"asks": [→ side{→ side"price": 85676.7,→ side · price"qty": 1.22854301→ side · quantity},{→ side"price": 85676.9,→ side · price"qty": 0.19258045→ side · quantity},{→ side"price": 85677.0,→ side · price"qty": 0.18888516→ side · quantity}],"checksum": 1875766410,"timestamp": "2026-10-05T19:18:01.569744Z"→ transaction_time}]}
Published rows · 6
received_time / 1,000,000). Not Kraken's timestamp.timestamp in milliseconds since the Unix epoch. Microseconds are truncated.BTC/USD.snapshot for type snapshot.bid for levels in bids, ask for levels in asks. One row per level.Book update
Each update lists the levels that changed. Here Kraken removes an ask and adds the ask that becomes the 1,000th level.
{"channel": "book","type": "update",→ event_type"data": [{"symbol": "BTC/USD",→ symbol"bids": [],"asks": [→ side{→ side"price": 85677.6,→ side · price"qty": 0.00000000→ side · quantity},{→ side"price": 88527.7,→ side · price"qty": 0.00223800→ side · quantity}],"checksum": 3606623782,"timestamp": "2026-10-05T19:18:01.705851Z"→ transaction_time}]}
Published rows · 2
received_time / 1,000,000). Not Kraken's timestamp.timestamp in milliseconds since the Unix epoch. Microseconds are truncated.BTC/USD.update for type update.bid for levels in bids, ask for levels in asks. One row per level.0.00000000 removes the level.Update that pushes a level out of the 1,000-level window
Kraken keeps each side at the subscribed 1,000 levels but sends no delete for a level that a new level pushes past the 1,000th. Clients are expected to drop it. The collector keeps each book per connection, finds the level that fell out and publishes it as a delete row (quantity 0) after the levels Kraken sent. Here the new bid at 85650.2 pushes out the 1,000th bid, 82879.7, which Kraken added one message earlier.
Replayed after 5 earlier messages (not shown) that set the state this message builds on.
Abridged: The example is replayed after the snapshot and the four updates between it and this message. The snapshot's 1,000 asks are left out of that setup; this message changes only bids.
{"channel": "book","type": "update",→ event_type"data": [{"symbol": "BTC/USD",→ symbol"bids": [→ side{→ side"price": 85650.2,→ side · price"qty": 2.91790845→ side · quantity}],"asks": [],"checksum": 2228140126,"timestamp": "2026-10-05T19:18:01.714665Z"→ transaction_time}]}
Published rows · 2
received_time / 1,000,000). Not Kraken's timestamp.timestamp in milliseconds since the Unix epoch. Microseconds are truncated.BTC/USD.update for every row, including the added delete row.bid for levels in bids. The added delete row is on the side whose window overflowed.0.Notes
event_timeis our receive time; Kraken's own time is intransaction_time.- Kraken's
checksum(CRC32 of the top 10 bids and asks) is not published. Spot book messages have no sequence numbers, so gaps cannot be detected from the rows. Replay inreceived_timeorder; every reconnect starts with a newsnapshot. - Before the 2026-09 data-integrity release, levels pushed out of the 1,000-level window had no delete row. For earlier files, keep only the best 1,000 levels per side after each update.
- Kraken spot files do not start with an hour-start checkpoint snapshot. Continue from the previous hour's book or wait for the next snapshot.
- Files written before the October 2026 pipeline update hold
event_timein nanoseconds; divide values above 10^14 by 10^6. They can also containsnapshotrows from REST depth snapshots (at most 500 levels, no sequence). Current files publish only the WebSocket book.
Column types and descriptions: Order Book schema.
Ticker
One row per pair per ticker message. Kraken sends an update after each trade.
- WebSocket ticker
Kraken sends a snapshot after subscribing and an update after every trade. Both map the same way.
{"channel": "ticker","type": "update","data": [{"symbol": "BTC/USD",→ symbol"bid": 85676.6,"bid_qty": 0.43273545,"ask": 85676.7,"ask_qty": 0.17561447,"last": 85676.6,→ last_price · open_price"volume": 2558.74399215,→ base_asset_volume"vwap": 85941.9,→ weighted_average_price"low": 84965.7,→ low_price"high": 86973.6,→ high_price"change": 300.7,→ price_change · open_price"change_pct": 0.35,→ price_change_percent"trades": 142899,"timestamp": "2026-10-05T19:18:02.218613Z"}]}
Published row
received_time / 1,000,000). Kraken's timestamp is not published.BTC/USD.0.35 means +0.35 %.last − change, computed as an exact decimal. Kraken does not send the 24-hour open.trades field is not published.Notes
event_timeis our receive time, not Kraken'stimestamp. Files written before the October 2026 pipeline update hold it in nanoseconds; divide values above 10^14 by 10^6.
Column types and descriptions: Ticker schema.
Not published
- Mark Price: Kraken spot has no mark price or funding.
- Open Interest: Spot markets have no open interest.
- Liquidations: Kraken does not publish spot liquidations.