Skip to main content
High-throughput sockets can opt into zstd compression. It’s negotiated once at the WebSocket upgrade and is connection-scoped: it applies to every room subscription on that connection, and can’t be changed without reconnecting. When enabled, room envelopes arrive as binary frames of zstd-compressed JSON rather than text frames. Everything else about the protocol is unchanged, only the wire format of inbound messages differs.

Enabling compression

Request compression at the handshake in one of two ways:
Omit both the header and the query parameter for the default uncompressed text frames. zstd is the only supported value; an unsupported value fails the upgrade with 400. Subscribe-message compression is not used, only the handshake matters.
The header is preferred because it keeps the compression choice out of the URL and logs. Use the query parameter only when your client can’t set upgrade headers, as is the case for the browser WebSocket constructor. If you set both, they must agree.

Decoding frames

A compressed connection delivers each room envelope as a binary frame. Decompress the bytes with zstd, then JSON.parse the resulting UTF-8 string exactly as you would an uncompressed text frame.
Browsers have no native zstd decoder (DecompressionStream supports only gzip and deflate), so a browser client needs a small library such as fzstd to decompress frames. The TypeScript SDK handles this for you.
Branch on the frame type rather than assuming every frame is binary. Text control frames such as the {"type":"pong"} keepalive reply still arrive as text, so a typeof data === "string" (or isBinary / instanceof ArrayBuffer) check keeps both paths working on the same connection.

Confirming the negotiated mode

Each room’s *_subscribe_response echoes a compression field reporting whether zstd delivery is active for the connection, so you can assert the negotiation succeeded without inspecting frame types.

When to use it

Compression trades a little CPU on both ends for lower bandwidth. It pays off most on high-volume subscriptions, verbose JSON payloads (metrics and order-book rooms), firehose subscriptions, and bandwidth-constrained or metered clients. For a handful of narrowly filtered rooms the savings are marginal, so leave it off unless you’re moving real volume.
The dashboard websockets playground has a zstd compression toggle on the connection card, so you can watch compressed delivery end to end before wiring it into your own client.

Next steps

Last modified on July 13, 2026