Skip to content

WebSocket Streaming

The server bridges streaming onto a local WebSocket at ws://127.0.0.1:25520/v1/events. Connect, send one JSON envelope per command, and receive one JSON message per event.

Subscribe envelope

json
{
    "msg_type": "STREAM",
    "sec_type": "STOCK",
    "req_type": "QUOTE",
    "add": true,
    "id": 1,
    "contract": {"symbol": "AAPL"}
}
FieldValues
sec_typeSTOCK, OPTION, INDEX
req_typeQUOTE, TRADE, OHLC, OPEN_INTEREST, FULL_TRADES, FULL_OPEN_INTEREST
addtrue subscribes, false unsubscribes
idYour request id; echoed in the acknowledgement
contractOmit for FULL_* streams

Option contracts carry the four-tuple. By default the strike is the terminal's 1/10-cent integer (a JSON integer, e.g. 570000 for a $570 strike); pass the server's --strike-format dollars flag to take a dollar value (570) instead:

json
{"symbol": "SPY", "expiration": 20250321, "strike": 570000, "right": "C"}

{"msg_type": "STOP", "id": 2} removes every active stream at once. Each command is acknowledged with a stream-request verification value in the response field:

json
{ "header": { "type": "REQ_RESPONSE", "response": "SUBSCRIBED", "req_id": 1 } }

SUBSCRIBED confirms the request was accepted; it acknowledges subscribe (add: true), unsubscribe (add: false), and STOP, since there is no removal-specific value. Rejected commands answer with "response": "ERROR" and an error field naming the cause — a bad envelope, an offending field, or a subscribe sent before streaming has started (which installs nothing, so it is never acknowledged as a success). The values MAX_STREAMS_REACHED and INVALID_PERMS are also part of the verification vocabulary.

Event messages

Events arrive as JSON with a header.type of QUOTE, TRADE, or OHLC, plus a STATUS heartbeat every second. OHLC bars arrive from upstream automatically — one per traded contract, ahead of that contract's trade; they are not derived from your subscriptions and are not subscribed to separately. An OPEN_INTEREST subscription is accepted, but open interest has no WebSocket frame: the data is delivered through the native SDK callbacks and the REST surface, not over this endpoint.

Try it

bash
websocat ws://127.0.0.1:25520/v1/events
{"msg_type": "STREAM", "sec_type": "OPTION", "req_type": "TRADE", "id": 1, "add": true, "contract": {"symbol": "SPY", "expiration": 20250321, "strike": 570000, "right": "C"}}

Limits

  • One client at a time. A second connection takes over the stream; the first receives a Close frame (code 1000, reason replaced by a new client connection). Run one server instance per consumer for multi-client setups.
  • Text frames are capped at 4 KiB — far above any legitimate envelope.
  • Each connection has a per-client send buffer (default 4096 events). On a high-rate stream a slow consumer that fills the buffer starts dropping events; raise the buffer with the THETADATADX_WS_CLIENT_CAPACITY environment variable (trades memory for headroom). See Environment variables.
  • Programmatic consumers should prefer the SDK streaming surface, which adds typed events, automatic reconnect, and drop monitoring.

Released under the Apache-2.0 License.