tastytradeDeveloper Docs
Legacy ↗

Streaming

tastytrade exposes two independent WebSocket streamers. They serve different purposes, use different tokens, and connect to different hosts:

  • The DXLink market-data streamer pushes live quotes, trades, Greeks, and candles from tastytrade's quote provider (dxFeed).
  • The account streamer pushes one-directional notifications about your account state (orders, balances, positions) plus non-account data: public watchlists and user-level quote alerts.

Both let you subscribe to real-time updates instead of polling REST endpoints. Streaming is for staying in sync; the REST API in /reference remains the source of truth and the only way to take action (place orders, etc.).

DXLink market-data streamer

1. Get an API quote token

Request a token with GET /api-quote-tokens. The token is tied to the customer identified by your OAuth access token, and the response includes the dxlink-url to connect to:

{
  "data": {
    "token": "<redacted>",
    "dxlink-url": "wss://tasty-openapi-ws.dxfeed.com/realtime",
    "level": "api"
  },
  "context": "/api-quote-tokens"
}

API quote tokens expire after 24 hours — refresh before they lapse. You must be a fully onboarded tastytrade customer; a username/password-only registration is rejected with quote_streamer.customer_not_found_error.

2. Connect and exchange messages

Open a WebSocket to the returned dxlink-url. Message order matters; the high-level handshake is:

  1. SETUP — initiate the connection (negotiate version and keepalive timeout).
  2. AUTH — after the server sends AUTH_STATE with state: UNAUTHORIZED, authorize with your API quote token.
  3. CHANNEL_REQUEST — open a channel (a virtual sub-connection; use separate channels to organize subscriptions, e.g. equities vs. futures).
  4. FEED_SETUP — configure which event fields to receive on the channel.
  5. FEED_SUBSCRIPTION — subscribe to (or remove) events for one or more symbols.
  6. KEEPALIVE — send periodically to hold the connection open.

A SETUP plus AUTH exchange looks like this:

{ "type": "SETUP", "channel": 0, "version": "0.1-DXF-JS/0.3.0", "keepaliveTimeout": 60, "acceptKeepaliveTimeout": 60 }
{ "type": "AUTH", "channel": 0, "token": "<redacted>" }

A channel configured for COMPACT data (requested via FEED_SETUP's acceptDataFormat) delivers events as positional arrays rather than verbose objects:

{ "type": "FEED_SUBSCRIPTION", "channel": 3, "reset": true, "add": [ { "type": "Quote", "symbol": "SPY" }, { "type": "Trade", "symbol": "SPY" } ] }
{ "type": "FEED_DATA", "channel": 3, "data": [ "Trade", [ "Trade", "SPY", 559.36, 13743299, 100.0 ] ] }

Common event types include Quote, Trade, Greeks, Summary, Profile, and Candle. Candle events return historical aggregates (open/high/low/close) — you supply a period and type in the symbol (for example AAPL{=5m}) plus a fromTime Unix-epoch timestamp.

Keepalive and quiet feeds

If DXLink does not receive a keepalive within its 60-second timeout, it closes the connection. Sending KEEPALIVE every ~30 seconds keeps it open indefinitely:

{ "type": "KEEPALIVE", "channel": 0 }

DXLink only publishes events as they occur. A connected, heartbeating socket that receives nothing usually means the symbol simply has no trading activity — that is normal, not a bug.

Symbology

You must subscribe using DXLink-formatted symbols. tastytrade provides these on instrument responses in the streamer-symbol field — for example a futures contract /6AM3 exposes streamer-symbol: "/6AM23:XCME". The same field is available across the instruments endpoints (GET /instruments/equities/{symbol}, GET /instruments/futures, GET /instruments/cryptocurrencies, GET /option-chains/{symbol}, GET /futures-option-chains/{symbol}). Prefer it over hand-building symbols. For more on tastytrade symbology (/ futures, ./ future options, OCC options, crypto pairs), see /reference.

Account streamer

The account streamer is a one-directional WebSocket that publishes notifications when your account data changes. Instead of re-fetching an order to learn it went from Routed to Filled, you receive a push message as the status changes.

Hosts

EnvironmentWebSocket host
Sandboxwss://streamer.cert.tastyworks.com
Productionwss://streamer.tastyworks.com

These pair with the REST hosts https://api.cert.tastyworks.com (sandbox) and https://api.tastyworks.com (production).

Authentication

Every message includes an auth-token — your tastytrade OAuth access token, the same value you send in the Authorization header for REST calls. Access tokens last 15 minutes (mint a fresh one with POST /oauth/token — see Get started for the auth flow), so refresh and re-authenticate over the long-lived socket as needed.

Connect, then heartbeat

Perform these steps in order — if you heartbeat before connecting you may get a not implemented error:

  1. Open the WebSocket connection.
  2. Send a connect message to subscribe to account updates.
  3. Send heartbeat messages on a 2s–1m interval.
{ "action": "connect", "value": [ "5WT00000", "5WT00001" ], "auth-token": "<access token>", "request-id": 2 }
{ "action": "heartbeat", "auth-token": "<access token>", "request-id": 1 }

The optional request-id is echoed back in the server's response so you can correlate messages. Other actions include public-watchlists-subscribe and quote-alerts-subscribe (note: quote alerts are scoped to the user, not an account).

Receiving notifications

Notifications use the same JSON object representations as the REST API and always contain the full object — never a partial or differential update. Each message carries a type and a data payload:

{
  "type": "Order",
  "data": {
    "id": 1,
    "account-number": "5WT00000",
    "status": "Live",
    "underlying-symbol": "AAPL",
    "ext-client-order-id": "67890"
  },
  "timestamp": 1688595114405
}

Note the dasherized JSON keys (account-number, ext-client-order-id), consistent with the rest of the API.

Streaming and orders

The account streamer reports order state — it does not place orders. Submit orders over REST, dry-run first (validate against POST /accounts/{account_number}/orders/dry-run), then send the live order with your own unique external-identifier so you can recognize an order you already submitted — the server does not deduplicate retries, and there is no idempotency-key header. On an uncertain outcome (timeout, 5xx), check GET /accounts/{account_number}/orders (or /orders/live) for that identifier before resubmitting. See Idempotency & retries. You then watch the streamer for Order notifications as the status advances.

Operational notes

  • Always send the User-Agent: product/version header on the REST calls that mint your tokens.
  • Reconnect with backoff on drops; respect the 60-second DXLink keepalive timeout and the account-streamer heartbeat window. See Rate limits & backoff.
  • Token errors (401/403) and other HTTP error codes (400/404/422/429/5xx) on the token endpoints are documented in /reference/errors.
  • For an end-to-end client that wraps both streamers, see MCP Server.

Machine-readable spec

Both streamers are described in an AsyncAPI 2.6 document — download it as JSON or YAML.