The order is what trips people up on a Massive WebSocket. A subscribe sent before the auth message is answered with auth_failed, naming an authentication problem even though the key was never sent, so a client that pipelines the two ends up re-copying a key that was fine. Wait for each reply: reading the three status messages is the fastest way to find what went wrong.
Applies to
- Plans: every paid subscription. Basic, the free tier, has no WebSocket access.
- Feeds: wss://socket.massive.com/stocks, /options, /indices, /futures, /forex and /crypto. One socket talks to one cluster.
- Asset classes: stocks, options, indices, futures, forex and crypto.
How to connect
- Open a socket to the cluster you need. The connection is accepted before any authentication, and the server greets you:
[{"ev":"status","status":"connected","message":"Connected Successfully"}]
- Send your key. Your key is on the keys page of the dashboard.
{"action":"auth","params":"KEY"}
Response
[{"ev":"status","status":"auth_success","message":"authenticated"}]
- Subscribe. A parameter is a channel and a ticker joined by a dot, and you can pass a comma-separated list or * for every ticker on that channel.
{"action":"subscribe","params":"T.AAPL,Q.MSFT"}
Response
[{"ev":"status","status":"success","message":"subscribed to: T.AAPL"},{"ev":"status","status":"success","message":"subscribed to: Q.MSFT"}]
One status object comes back per parameter, in one array, so a parser that reads only the first element will miss the rest. Data messages start arriving straight after the acknowledgement, each one a JSON array that often carries several events.
Which channel names to use
Channel names are per cluster, and using another cluster's name is the most common reason a subscribe is refused. The WebSocket documentation lists every channel; these are the ones asked about most:
| Channel | Cluster | What it carries |
|---|---|---|
| T | stocks, options, futures | Individual trades |
| Q | stocks, options, futures | Quotes |
| A | stocks, options, indices, futures | Per-second aggregate bars |
| AM | stocks, options, indices, futures | Per-minute aggregate bars |
| XT, XQ | crypto | Trades and quotes |
| XAS, XA | crypto | Per-second and per-minute aggregate bars |
| C | forex | Quotes. Forex has no trade feed |
| CAS, CA | forex | Per-second and per-minute aggregate bars |
| V | indices | Index values |
Why the order matters
The order matters because the server tracks a connection's state. A subscribe sent before the auth message is answered with auth_failed and the socket is closed, even though the key was never sent, so wait for auth_success rather than pipelining the two. Authenticating twice on one socket is not how you add a second stream either: one socket carries as many subscriptions as you like, so subscribe again on the same socket instead of opening another. Opening another socket spends one of your connections, and if that takes you over your plan's allowance for that cluster, one of the two connections is closed.
If you see an error
{"ev":"status","status":"auth_failed","message":"authentication failed"} means either the key was rejected or a subscribe arrived before the auth message. The server closes the socket either way. Check the order first, because a client that sends both messages without waiting for auth_success produces this frame with a perfectly good key.
{"ev":"status","status":"error","message":"not authorized"} in reply to a subscribe means either your plan does not cover that feed, or you used a channel that exists on another cluster but not this one. A on the crypto cluster gets this, because crypto uses XAS.
Nothing at all after auth_success is the other failure, and it is quieter. A channel name that exists nowhere, or a channel from the wrong cluster that we do not recognize at all, is not acknowledged and not refused: the server simply says nothing. If a subscribe gets no reply, check the spelling against the table above rather than waiting.

