Live stock prices stream
Requires no token and no scope. Prices are public, like the market data they come from.
GET …/stocks/list gives you a price that is up to a
minute old. The stream pushes a stock's price each time the venue updates it,
which is about once a second while its market is open, over one long-lived
WebSocket connection:
Code
It is a plain RFC 6455 WebSocket with JSON text messages. You don't need an SDK. Any WebSocket client in any language works.
Quick start
Code
Messages you send
Every message is a JSON object with an op. reqId is optional. It can be a
string of up to 64 characters or an integer, and it is echoed on the reply so
you can match the two.
op | Fields | Reply |
|---|---|---|
subscribe | channel, symbols | subscribed |
unsubscribe | channel, symbols | unsubscribed |
ping | none | pong |
Requests are answered in the order you sent them. An unsubscribe sent
right after a subscribe always takes effect second.
Messages you receive
Every message is a JSON object with a type.
type | When |
|---|---|
subscribed | A subscribe was applied |
unsubscribed | An unsubscribe was applied |
pong | Reply to a ping |
update | Channel data, see below |
error | A request was refused. The connection stays open |
The stock_price channel
Subscribing
Code
Code
symbols are the symbol field of the catalogue
(AAPLon), not the slug (apple). They are matched case-insensitively.
symbols in the reply lists everything you are now subscribed to, spelled the
way the catalogue spells it, and that is the spelling updates arrive under.
A symbol that is not in the catalogue is listed in unknownSymbols and
skipped. It does not fail the rest of the request, so a list that has gone
stale still subscribes to everything that exists. Log unknownSymbols rather
than ignoring it.
One message can name up to 1,000 symbols, which covers the whole catalogue. Subscribing again to a symbol you already have is harmless.
Updates
Code
Each item in data is a [symbol, price, timestamp] array:
| Position | Meaning |
|---|---|
0 | The symbol, as the catalogue spells it |
1 | The token's price in USD, to five decimal places. This is the same figure …/stocks/list reports as price, which rounds it to two |
2 | When the venue produced this price, in unix milliseconds. It is not when we sent it |
Three things about delivery to design around:
- Updates are batched. Prices are gathered for up to 100 ms, then sent together, so one message can carry any number of symbols.
- Only the latest price per symbol is sent. If a symbol updates twice within one batch, you get the second price only. This stream reports the current price, not every trade.
- You get a price straight after subscribing if we already hold one. It
arrives as a normal
updateright after thesubscribedreply, and its timestamp tells you how old it is.
A price that stops moving is usually a shut market
Outside a stock's trading sessions the venue sends nothing for it, so its last
update can be hours old. Read marketOpen and nextOpenAt from
…/stocks/list before
concluding the stream is broken.
Not every commodity streams
Tether Gold (XAUt) is in the catalogue and subscribes without an error, but
this stream never sends an update for it, although its market is always open.
Poll its price instead.
Unsubscribing
Code
Code
The reply echoes the symbols you sent. Unsubscribing from a symbol you never subscribed to does nothing and returns no error. An update already on its way can still arrive just after the reply.
Staying connected
Subscriptions belong to the connection. When the connection closes they are gone, and a new connection starts with none. Your client needs to:
- reconnect when the socket closes, with a backoff;
- send its
subscribeagain on every new connection.
A reconnect costs you nothing. You get the current prices straight after the
new subscribed reply.
Connections are closed on purpose, not only when something fails. Every deploy
closes them with code 1001. Treat a close as routine and reconnect.
Heartbeats. We send a WebSocket ping every 30 seconds. A connection that has
not answered one by the next ping is dropped. Most WebSocket libraries answer
pings automatically. If yours doesn't expose them (browsers never do), send
{"op":"ping"} instead. Any message you send counts as a sign of life.
Keep reading. If you stop reading from the socket, we stop writing updates to it and keep only the latest price per symbol. Once you catch up you get those latest prices, not a backlog. A client that falls far enough behind to stop answering pings is disconnected.
Example: Node.js
Code
Example: Python
Code
Errors
A refused request is answered with an error message, and the connection stays
open:
Code
reqId is included whenever the request carried a usable one.
code | Cause |
|---|---|
BAD_REQUEST | The message was not a JSON object, was sent as a binary frame, or had a missing or invalid field |
UNKNOWN_CHANNEL | No channel has that name |
UNAVAILABLE | The channel cannot serve right now. Retry later |
INTERNAL_ERROR | Something failed on our side. Retry later |
Some problems end the connection instead:
| What you see | Cause |
|---|---|
HTTP 503 instead of the WebSocket handshake | The server you reached is at capacity. Wait for the Retry-After header and reconnect |
Close code 1001 | A deploy or restart. Reconnect straight away |
Close code 1008 | You sent more than 100 requests without waiting for replies |
Close code 1009 | A message was larger than 64 KB |
Close with no code (1006) | The connection dropped, or stopped answering pings. Reconnect |
