Exchange Wire Formats

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

WebSocket message · tradereceived 2026-10-05 19:18:02.245217 UTC
{
"channel": "trade",
"type": "update",
"data": [
{
"symbol": "BTC/USD",
"side": "sell",
"price": 85676.6,
"qty": 0.00001764,
"ord_type": "limit",
"trade_id": 110284812,
"timestamp": "2026-10-05T19:18:02.218613Z"
}
]
}

Published row

received_time1791227882245217000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791227882218data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated. Same value as trade_time.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
trade_id110284812data[].trade_idCopied. Kraken numbers each pair's trades consecutively, so a jump is a gap.
price85676.6data[].priceCopied with the exact digits of Kraken's JSON number, trailing zeros included.
quantity0.00001764data[].qtyCopied with the exact digits of Kraken's JSON number, trailing zeros included. Base asset units.
trade_time1791227882218data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated. If the timestamp does not parse, our receive time in milliseconds is used.
is_buyer_makertruedata[].sidetrue when side is sell. Kraken's side is the taker's side.
order_typelimitdata[].ord_typeCopied: the taker order's type, 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.

WebSocket message · tradereceived 2026-10-05 20:00:09.717900 UTC
{
"channel": "trade",
"type": "snapshot",
"data": [
{
"symbol": "BTC/USD",
"side": "sell",
"price": 85706.1,
"qty": 0.00023200,
"ord_type": "limit",
"trade_id": 110288956,
"timestamp": "2026-10-05T19:59:55.347091Z"
},
{
"symbol": "BTC/USD",
"side": "buy",
"price": 85706.0,
"qty": 0.00043755,
"ord_type": "limit",
"trade_id": 110288957,
"timestamp": "2026-10-05T19:59:57.436061Z"
},
{
"symbol": "BTC/USD",
"side": "sell",
"price": 85723.2,
"qty": 0.00001322,
"ord_type": "limit",
"trade_id": 110289005,
"timestamp": "2026-10-05T20:00:08.679839Z"
}
]
}

Published rows · 3

received_time1791230409717900000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791230395347data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated. Same value as trade_time.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
trade_id110288956data[].trade_idCopied. Kraken numbers each pair's trades consecutively, so a jump is a gap.
price85706.1data[].priceCopied with the exact digits of Kraken's JSON number, trailing zeros included.
quantity0.00023200data[].qtyCopied with the exact digits of Kraken's JSON number, trailing zeros included. Base asset units.
trade_time1791230395347data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated. If the timestamp does not parse, our receive time in milliseconds is used.
is_buyer_makertruedata[].sidetrue when side is sell. Kraken's side is the taker's side.
order_typelimitdata[].ord_typeCopied: the taker order's type, 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.

REST responsereceived 2026-10-05 19:20:13.148740 UTC
GET https://api.kraken.com/0/public/Trades?pair=BTC/USD&since=1791227953000000000&count=3
{
"error": [],
"result": {
"BTC/USD": [
["85788.50000", "0.00043713", 1791227953.2866168, "s", "l", "", 110285002],
["85788.50000", "0.00010000", 1791227953.2866168, "s", "l", "", 110285003],
["85783.20000", "0.00043715", 1791227953.6063356, "s", "l", "", 110285004]
],
"last": "1791227953606335684"
}
}

Published row

received_time1791228013148740113—Our collector's clock when it wrote the recovered trades, in nanoseconds since the Unix epoch. Later than the trade itself.
event_time1791227953286result.BTC/USD[][2]The REST time in seconds, cut to microseconds as an RFC 3339 time, then milliseconds since the Unix epoch. Same value as trade_time.
symbolBTC/USD—The pair the collector requested.
trade_id110285002result.BTC/USD[][6]Copied.
price85788.50000result.BTC/USD[][0]Copied with the exact digits of Kraken's REST decimal string, so REST rows keep trailing zeros (85788.50000) that WebSocket rows do not have.
quantity0.00043713result.BTC/USD[][1]Copied with the exact digits of the REST decimal string. Base asset units.
trade_time1791227953286result.BTC/USD[][2]The REST time in seconds, cut to microseconds, then milliseconds since the Unix epoch.
is_buyer_makertrueresult.BTC/USD[][3]true when the side is s (taker sold).
order_typelimitresult.BTC/USD[][4]limit for l, market for m.

Notes

  • trade_id is 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.

WebSocket message · bookreceived 2026-10-05 19:18:01.660108 UTC
{
"channel": "book",
"type": "snapshot",
"data": [
{
"symbol": "BTC/USD",
"bids": [
{
"price": 85676.6,
"qty": 0.09509427
},
{
"price": 85675.9,
"qty": 0.04469152
},
{
"price": 85675.8,
"qty": 0.17507841
}
],
"asks": [
{
"price": 85676.7,
"qty": 1.22854301
},
{
"price": 85676.9,
"qty": 0.19258045
},
{
"price": 85677.0,
"qty": 0.18888516
}
],
"checksum": 1875766410,
"timestamp": "2026-10-05T19:18:01.569744Z"
}
]
}

Published rows · 6

received_time1791227881660108000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791227881660—Our receive time in milliseconds (received_time / 1,000,000). Not Kraken's timestamp.
transaction_time1791227881569data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
event_typesnapshottypesnapshot for type snapshot.
first_update_idnull—Null. Kraken spot book messages have no sequence numbers.
final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
prev_final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
last_update_idnull—Null. Kraken spot book messages have no sequence numbers.
sidebiddata[].bidsdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85676.6data[].bids[].pricedata[].asks[].priceCopied with the exact digits of Kraken's JSON number, trailing zeros included.
quantity0.09509427data[].bids[].qtydata[].asks[].qtyCopied with the exact digits of Kraken's JSON number, trailing zeros included. Base asset units.
order_countnull—Null. Kraken does not report order counts.

Book update

Each update lists the levels that changed. Here Kraken removes an ask and adds the ask that becomes the 1,000th level.

WebSocket message · bookreceived 2026-10-05 19:18:01.726469 UTC
{
"channel": "book",
"type": "update",
"data": [
{
"symbol": "BTC/USD",
"bids": [],
"asks": [
{
"price": 85677.6,
"qty": 0.00000000
},
{
"price": 88527.7,
"qty": 0.00223800
}
],
"checksum": 3606623782,
"timestamp": "2026-10-05T19:18:01.705851Z"
}
]
}

Published rows · 2

received_time1791227881726469000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791227881726—Our receive time in milliseconds (received_time / 1,000,000). Not Kraken's timestamp.
transaction_time1791227881705data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
event_typeupdatetypeupdate for type update.
first_update_idnull—Null. Kraken spot book messages have no sequence numbers.
final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
prev_final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
last_update_idnull—Null. Kraken spot book messages have no sequence numbers.
sideaskdata[].asksbid for levels in bids, ask for levels in asks. One row per level.
price85677.6data[].asks[].priceCopied with the exact digits of Kraken's JSON number, trailing zeros included.
quantity0.00000000data[].asks[].qtyCopied with the exact digits of Kraken's JSON number, trailing zeros included. The new total at that price; 0.00000000 removes the level.
order_countnull—Null. Kraken does not report order counts.

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.

WebSocket message · bookreceived 2026-10-05 19:18:01.735746 UTC
{
"channel": "book",
"type": "update",
"data": [
{
"symbol": "BTC/USD",
"bids": [
{
"price": 85650.2,
"qty": 2.91790845
}
],
"asks": [],
"checksum": 2228140126,
"timestamp": "2026-10-05T19:18:01.714665Z"
}
]
}

Published rows · 2

received_time1791227881735746000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791227881735—Our receive time in milliseconds (received_time / 1,000,000). Not Kraken's timestamp.
transaction_time1791227881714data[].timestampKraken's RFC 3339 timestamp in milliseconds since the Unix epoch. Microseconds are truncated.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
event_typeupdatetypeupdate for every row, including the added delete row.
first_update_idnull—Null. Kraken spot book messages have no sequence numbers.
final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
prev_final_update_idnull—Null. Kraken spot book messages have no sequence numbers.
last_update_idnull—Null. Kraken spot book messages have no sequence numbers.
sidebiddata[].bidsbid for levels in bids. The added delete row is on the side whose window overflowed.
price85650.2data[].bids[].priceCopied with the exact digits of Kraken's JSON number, trailing zeros included. The added delete row has the price of the level that left the window, as Kraken last sent it.
quantity2.91790845data[].bids[].qtyCopied with the exact digits of Kraken's JSON number, trailing zeros included. The added delete row has 0.
order_countnull—Null. Kraken does not report order counts.

Notes

  • event_time is our receive time; Kraken's own time is in transaction_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 in received_time order; every reconnect starts with a new snapshot.
  • 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_time in nanoseconds; divide values above 10^14 by 10^6. They can also contain snapshot rows 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.

WebSocket message · tickerreceived 2026-10-05 19:18:02.242456 UTC
{
"channel": "ticker",
"type": "update",
"data": [
{
"symbol": "BTC/USD",
"bid": 85676.6,
"bid_qty": 0.43273545,
"ask": 85676.7,
"ask_qty": 0.17561447,
"last": 85676.6,
"volume": 2558.74399215,
"vwap": 85941.9,
"low": 84965.7,
"high": 86973.6,
"change": 300.7,
"change_pct": 0.35,
"trades": 142899,
"timestamp": "2026-10-05T19:18:02.218613Z"
}
]
}

Published row

received_time1791227882242456000—Our collector's clock when the message arrived, in nanoseconds since the Unix epoch. Not sent by Kraken.
event_time1791227882242—Our receive time in milliseconds (received_time / 1,000,000). Kraken's timestamp is not published.
symbolBTC/USDdata[].symbolCopied, for example BTC/USD.
price_change300.7data[].changeCopied with the exact digits of Kraken's JSON number, trailing zeros included. The 24-hour change in quote currency.
price_change_percent0.35data[].change_pctCopied with the exact digits of Kraken's JSON number, trailing zeros included. Percent: 0.35 means +0.35 %.
weighted_average_price85941.9data[].vwapCopied with the exact digits of Kraken's JSON number, trailing zeros included. 24-hour VWAP.
last_price85676.6data[].lastCopied with the exact digits of Kraken's JSON number, trailing zeros included.
last_quantitynull—Null. Kraken's ticker has no last trade size.
open_price85375.9data[].lastdata[].changelast − change, computed as an exact decimal. Kraken does not send the 24-hour open.
high_price86973.6data[].highCopied with the exact digits of Kraken's JSON number, trailing zeros included.
low_price84965.7data[].lowCopied with the exact digits of Kraken's JSON number, trailing zeros included.
base_asset_volume2558.74399215data[].volumeCopied with the exact digits of Kraken's JSON number, trailing zeros included. 24-hour volume in base asset units.
quote_asset_volumenull—Null. Kraken's ticker has no quote volume.
statistics_open_timenull—Null. Kraken does not send the window bounds; the window is the last 24 hours.
statistics_close_timenull—Null. Kraken does not send the window bounds.
first_trade_idnull—Null. Not sent by Kraken.
last_trade_idnull—Null. Not sent by Kraken.
total_tradesnull—Null. Kraken's trades field is not published.

Notes

  • event_time is our receive time, not Kraken's timestamp. 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.