Skip to main content
The market data WebSocket uses Protocol Buffers (protobuf) for binary message encoding. This page is where you get the .proto files, compile them for your language, and see working decoder code for the frames the gateway sends. Read it before your first connection to the stream; come back to it whenever a field decodes to a value that looks wrong by orders of magnitude.

Getting the Proto Files

Download all proto files (zip) Or download individual files and place them in a proto/kairos/v1/ directory: The synthetic schema belongs under proto/kairos/synthetic/v1/, not proto/kairos/v1/. Synthetic data frames also carry a version byte after their type tag. See the Synthetic Book Stream tutorial. Your local directory structure should look like:

Compiling Proto Files

Python

Go

C++

TypeScript / JavaScript

Rust

Add to Cargo.toml:
In build.rs:

OrderbookSnapshot carries its own scales

OrderbookSnapshot has two fields that a naive decoder will miss: Every prediction-market provider sends 0 for both, so existing decoders are unaffected.
Gotcha: perpetual snapshots set the scales per snapshot. A consumer that hard-codes 10,000 will read perpetual prices wrong by orders of magnitude. Always divide by the snapshot’s own scale:
OrderbookDelta has no scale fields — it is only emitted by providers that publish on the default scale. Perpetual books publish whole baselines, never deltas, which is what makes a per-snapshot scale safe: two snapshots at different scales are never merged.

Both carry an ownership epoch

The epoch is bumped when ownership of a contract transfers between streamer instances. 0 means unfenced (legacy publishers).
Gotcha: drop any frame from a lower epoch. Two streamer instances have independent sequence spaces, so a decoder must discard any frame whose epoch is below the highest it has seen for that contract — otherwise a brief double-publish during a handoff collides the two sequence spaces and freezes the book.
Full rationale and the perpetual encoding rules are in Price and size scaling.

Reading Messages in Code

Every binary WebSocket message has a 1-byte type tag prefix.
Always dispatch on the type tag first. Never assume the payload is a specific message type. New tags may be added in future versions — clients that decode blindly will break.
Gotcha: a sequence gap means a full resync, with no tolerance. The example below stops applying deltas the moment delta.seq runs ahead of expected_seq (or snapshot_seq stops matching) and waits for a fresh snapshot from a FetchRequest. Skipping that check silently corrupts the local book by mixing levels from two baselines. See Sequencing and recovery.
Example in Python:

Handling ErrorResponse (tag 0x17)

The server reports in-band, recoverable errors (malformed subscribe, unknown or unauthorized topic, rate limit, subscription limit, failed snapshot recovery, unknown tag, text frame received) as an ErrorResponse protobuf: The two fetch_* forms embed the market they refer to, so match action by prefix rather than equality. The full catalogue of messages is in Market Data → In-band ErrorResponse. The connection stays open after an ErrorResponse — only the offending message is dropped. Decode and surface it for diagnostics:
For server-initiated close codes (4001 JWT expiry/revocation, 4002 anonymous session lifetime, 4003 anonymous lease lost, 1006 slow consumer / idle timeout), upgrade-time 401/429/503 rejections, and recommended reconnect behavior, see Market Data → Errors & Disconnection.

Sending a Subscribe Request