Beta β rolling out per region. zstd is being enabled gradually. If a gateway has it
disabled, a
receiveType: "zstd" or "zstd-dict" connection is gracefully downgraded to JSON β
the server tells you the negotiated mode in login_ok.receiveType. Always trust that echo to
choose your decoder; never assume the mode you requested.Two modes
Both compress every data frame with zstd; control frames stay JSON text. Pick based on how much client work you want to do:
Start with
"zstd". It needs no dictionary handling at all β decompress and parse, done. Move to
"zstd-dict" when you want the extra ~30β40% and can keep a small in-memory dictionary store.
There is intentionally no compressed-MessagePack mode β compressing JSON beats compressing MessagePack at every level.
Mode 1 β zstd (dictless)
The soft-adoption path. Every data frame is a standalone, dictless zstd frame (dictId = 0); the
server sends no dict control frames. Your client only decompresses and parses.
Login
Decode
Mode 2 β zstd-dict (trained dictionaries)
The maximum-ratio path. odds, fixtures, and bookmakers have trained ~32 KB dictionaries that
push the ratio to ~7β9Γ. The server delivers them as control frames at connect; each data frame
embeds the dictId it was compressed with, so decoding is self-describing and never branches on
channel.
Login
Dictionary delivery (server β client)
Right afterlogin_ok (and before any data frame), for each subscribed channel that has a trained
dictionary the server pushes one control frame:
- Base64-decode
datato the raw dictionary bytes. - Build a reusable zstd decoder from it, stored keyed by
dictId.
Decoding is driven by the
dictId embedded in each data frame, not by channel. The
channel/dictVersion fields on the dict frame are informational.dicts field to send at login. Channels without a trained
dictionary (e.g. scores, clocks) send no dict frame; their data frames carry dictId = 0 and
decode dictless.
Decode
Frame rules (both modes)
- Every data frame is a WebSocket Binary frame containing one standalone zstd frame
(magic
28 B5 2F FD, with an embeddeddictId). - Control frames (
login_ok,dict,error,snapshot_required,resume_complete) stay JSON text frames, even on a zstd connection. The rule is fixed:
Text frame β control (JSON). Binary frame β data (zstd).
login_ok.receiveType is the negotiated mode ("zstd", "zstd-dict", or β if the gateway has
zstd disabled β "json"). If it comes back "json", decode plain JSON text frames; do not try
to decompress.
π Python examples
Requirespip install zstandard.
- zstd (dictless)
- zstd-dict (dictionaries)
See also
- Auth & Filters β full
loginfield reference - Resume & Replay β reconnecting and recovering missed data
- Troubleshooting β zstd decoding issues