EODHD offers one of the best ways for investors, developers, and data analysts to incorporate real-time finance data for the stocks, 1100+ Forex pairs, 1000+ Digital Currencies into their decision-making projects with a delay of less than 50ms via WebSockets. For stocks our real-time data API supports pre-market and post-market hours (from 4 am to 8 pm ET; the session is computed in the America/New_York timezone, so those clock times hold all year regardless of daylight saving).
The subscription provides real-time access to prices for 50 tickers simultaneously via WebSockets. Additionally, the 50-symbol limit can be increased through the user dashboard.
For global markets and all exchanges around the world we also offer Live (Delayed) data with 15min delay.
What is WebSocket protocol
WebSockets is a communication protocol that provides full-duplex communication channels over a single TCP connection. It enables real-time communication between a client and a server, allowing them to exchange messages continuously without the overhead of repeatedly establishing new connections. In the case of financial data providers, Websockets API provides real-time stock market information with minimal delay.
Because real-time WebSocket streaming is resource-intensive, it is offered on paid plans rather than a free tier.
What you get (Data availability)
Stocks (full list of tickers available)
• Trade stream (last price, size, conditions, etc.)
• Quote stream (bid/ask, sizes)
• Extended hours supported (pre‑ and post‑market)
FOREX (full list of available currency pairs)
• Bid/ask + day change/difference
• Tickers like EURUSD, AUDUSD, etc.
Digital Currencies (Full list of available crypto pairs)
• Last price, quantity, day change/difference
• Tickers like ETH-USD, BTC-USD
Lists of available tickers and pairs for Real-Time feeds can be retrieved in JSON format via this API endpoint, providing the full list of exchange or asset class components (use “US” for US stocks, “CC” for cryptocurrencies, “FOREX” for currency pairs).
Demo access is available with API key “demo” for: AAPL, MSFT, TSLA, EURUSD, ETH-USD, BTC-USD.
Data source and what the data represents
Real-time US and European equities are delivered directly from Cboe Global Cloud, our official exchange data source. Because this is a direct-from-exchange feed rather than an aggregated one, it is worth being precise about what it contains.
US equities
US trades and quotes come from Cboe EDGX, one of Cboe’s four US equity exchanges. Trades are executions that occurred on EDGX — this is not a consolidated (SIP) tape, so it does not include executions from other exchanges, or off-exchange and FINRA TRF prints. Quotes are the EDGX top of book with sizes, not a national best bid and offer aggregated across venues. Pre-market and post-market trades are delivered and flagged through the market status field.
On a normal trading session the US stream carries about 12,700 symbols. Measured over a longer window the count is slightly higher — around 13,100 across 21 trading days — because a symbol that does not print on a given day is simply absent from that day’s stream.
European equities
European trades and quotes come from Cboe Europe, across its BXE, CXE and DXE order books, covering UK-listed names and continental large-caps over a single endpoint. Trading hours follow each instrument’s home venue, so the last trade for a symbol may lag once that venue enters its closing auction while quotes continue elsewhere.
Coverage spans 18 European markets and about 9,400 symbols on a normal session — London, XETRA, the Euronext venues, the Nordic exchanges, Madrid, Milan, Vienna and Warsaw among them. As with the US stream, a longer window gives a slightly higher count.
European quotes differ from the US ones in one important way: they are a consolidated best bid and offer across all three books. The best price wins, and where two books show the same price their sizes are added, so the size you see is the depth available at that price across the venues we carry.
Updates & Latency
- Streaming updates pushed in real time over a persistent WebSocket connection.
- Typical transport latency < 50 ms from our gateway to your client (excluding network distance).
- Market status flag indicates open, closed, or extended hours for stocks.
Endpoints
Open connection – use wss:// in production. ws:// is available for local testing.
Equities (Trades and Quotes):
wss://ws.eodhistoricaldata.com/ws/us?api_token=YOUR_API_KEY
wss://ws.eodhistoricaldata.com/ws/us-quote?api_token=YOUR_API_KEY
European Equities (Trades and Quotes):
wss://ws.eodhistoricaldata.com/ws/eu?api_token=YOUR_API_KEY
wss://ws.eodhistoricaldata.com/ws/eu-quote?api_token=YOUR_API_KEY
Minute Bars (US and Europe):
wss://ws.eodhistoricaldata.com/ws/us-candles?api_token=YOUR_API_KEY
wss://ws.eodhistoricaldata.com/ws/eu-candles?api_token=YOUR_API_KEY
Trading Status (US and Europe):
wss://ws.eodhistoricaldata.com/ws/us-status?api_token=YOUR_API_KEY
wss://ws.eodhistoricaldata.com/ws/eu-status?api_token=YOUR_API_KEY
Forex
wss://ws.eodhistoricaldata.com/ws/forex?api_token=YOUR_API_KEY
Crypto
wss://ws.eodhistoricaldata.com/ws/crypto?api_token=YOUR_API_KEY
Subscribe / Unsubscribe
After the socket is open, send JSON commands.
Connection and status messages: on a successful connection the server first sends an authorization message. If you subscribe to a symbol your plan does not allow, for example a non-demo ticker on the demo key, the server replies with a status message instead of data.
{"status_code": 200, "message": "Authorized"}
{"status_code": 422, "message": "Only limited symbols allowed for demo"}
Subscribe:
{"action": "subscribe", "symbols": "ETH-USD"}
Multiple symbols (comma‑separated):
{"action": "subscribe", "symbols": "AAPL,TSLA"}
Unsubscribe:
{"action": "unsubscribe", "symbols": "ETH-USD,BTC-USD"}
Response Schemas
Trades (endpoint: /ws/us):
{
"s": "AAPL", // ticker
"p": 309.19, // last trade price
"c": [], // trade conditions (array) - empty on this feed, see note
"v": 2, // trade size (shares)
"dp": false, // dark pool flag - always false on this feed, see note
"ms": "extended-hours", // market status: open | closed | extended-hours
"t": 1787671313868 // epoch ms, UTC
}
Two fields need a word of explanation. The conditions field c is an array, and on this feed it is always empty: the exchange top-of-book source does not carry per-trade sale conditions. The field is kept so the message shape stays identical across all our equity streams. The dark-pool flag dp is always false, because this is a single lit-exchange feed with no off-exchange or FINRA TRF prints in it.
Quotes (endpoint: /ws/us-quote):
{
"s": "AAPL", // ticker
"ap": 317.297, // ask price
"as": 160, // ask size
"bp": 316.988, // bid price
"bs": 40, // bid size
"t": 1784115291977 // epoch ms
}
European Equities (endpoints: /ws/eu and /ws/eu-quote):
European equities use exactly the same schemas as US equities — the trade stream matches /ws/us and the quote stream matches /ws/us-quote. Only the symbol format differs. Example trade message:
{
"s": "GSK.LSE", // ticker (TICKER.EXCHANGE)
"p": 1911, // last trade price, in the venue's quoting units
"c": [], // trade conditions (array) - empty on this feed
"v": 155, // trade size (shares)
"dp": false, // dark pool flag - always false on this feed
"ms": "open", // market status: open | closed
"t": 1787671304945 // epoch ms, UTC
}
Prices are JSON numbers and may arrive with no decimal part when the venue quotes in whole units, as above. Do not assume a fractional component.
Minute Bars (endpoints: /ws/us-candles and /ws/eu-candles):
A rolling one-minute OHLCV bar per subscribed symbol, built from the trade stream.
{
"s": "AAPL", // ticker
"i": "1m", // bar interval
"t": 1787671260000, // bar OPEN time, epoch ms UTC
"o": 309.25, // open
"h": 309.26, // high
"l": 309.19, // low
"c": 309.19, // close
"v": 1101 // volume in shares, accumulated within the bar
}
The field c means two different things depending on the stream: on the trade streams it is the conditions array, and on the minute-bar streams it is the bar close price. The current bar is also re-sent every time it changes, so the same bar-open timestamp arrives repeatedly with a growing volume until the minute rolls over. Treat the newest message for a given timestamp as authoritative rather than adding them together.
Trading Status (endpoints: /ws/us-status and /ws/eu-status):
Trading-status and halt messages for a subscribed symbol. This is a separate stream from the market status field ms carried inside each trade: ms tells you which session the market is in, while this stream reports the venue’s own status for the individual instrument.
{
"s": "AAPL", // ticker
"h": "T", // trading status code from the venue
"r": "0", // status reason code
"t": 1787731200000 // epoch ms, UTC
}
While an instrument is trading normally you will see the status T, with an empty reason on the European stream and 0 on the US one. Other codes are issued by the venue when an instrument is halted, paused or in an auction, and they are the venue’s own values rather than something we translate — so treat any status other than T as a signal to stop relying on the last price until it returns to T.
As with the other streams, you receive one message per symbol immediately on subscribing, so you can read the current status without waiting for it to change.
FOREX (endpoint: /ws/forex):
{
"s": "EURUSD", // symbol
"a": 1.1419, // ask
"b": 1.1416, // bid
"dc": "0.3005", // daily change, % (string)
"dd": "0.0034", // daily difference, price units (string)
"ppms": true, // pre/post market status flag
"t": 1784115288241 // epoch ms
}
Crypto (endpoint: /ws/crypto):
{
"s": "ETH-USD", // symbol
"p": "1881.0931", // last price (string)
"q": "1.9e-7", // trade quantity (string, may use exponent notation)
"dc": "5.6041", // daily change, % (string)
"dd": "105.4177", // daily difference, price units (string)
"t": 1784115286805 // epoch ms
}
Symbol Limits & Usage Notes
Concurrent subscriptions: up to 50 symbols per connection by default (upgradeable in user dashboard for extra fee).
Tickers:
- US Stocks: use plain tickers like AAPL, MSFT — no exchange suffix
- European Stocks: use TICKER.EXCHANGE, e.g., GSK.LSE, SAP.XETRA, ASML.AS — the native Cboe symbol is accepted too
- Forex: no separator, e.g., EURUSD
- Crypto: dash‑separated, e.g., BTC-USD
Resubscribe on reconnect: if the socket reconnects, re‑send your current subscriptions.
Compression/Throughput: consider batching symbol lists in a single subscribe call for efficiency.
Symbols that never arrive
A symbol that is unknown, or correct-looking but in the wrong format for the market, is accepted silently — no error is returned and the symbol simply never streams. If one symbol arrives and others stay quiet, check the format of the quiet ones first. The most common cause is an exchange suffix on a US ticker, or a missing one on a European ticker.
One input mistake does produce an error: sending the symbols value as a JSON array instead of a comma-separated string is rejected with a 422.
European tickers containing a hyphen
European tickers that contain a hyphen — mostly Nordic dual-class shares such as ERIC-B.ST or NOVO-B.CO — work under their normal EODHD name. The un-hyphenated form, for example ERICB.ST, is also accepted as an alias, so client code written against either spelling keeps working. Whichever form you subscribe with, every message comes back carrying the canonical hyphenated ticker in the s field — subscribe to ERICB.ST and the data arrives as ERIC-B.ST. Key your state on the s value you receive rather than on the string you sent, or the two will not match. Note that some hyphenated tickers are not carried on the venues behind this feed at all and return nothing in either form.
Connection limits
You may hold up to 64 concurrent connections per API token. That budget is shared across every market — trades, quotes, minute bars, forex and crypto sockets all count toward the same total. Opening one beyond it is refused with a 429 before the WebSocket opens.
Opening a separate connection per stream is expected and supported. The cap exists so one runaway client cannot degrade the feed for everyone, so if you legitimately need more, contact support rather than retrying in a loop.
Minute Bars over REST
Closed one-minute bars for a single symbol are also available over plain HTTPS. This is the straightforward way to fill a gap after a dropped connection, rather than waiting for the streams to rebuild.
GET https://ws.eodhistoricaldata.com/history?market=us&symbol=AAPL&api_token=YOUR_API_KEY
The response is a JSON array of closed bars, oldest first, in the same shape as the minute-bar streams:
[
{"s":"AAPL","i":"1m","t":1787701620000,"o":309.32,"h":309.32,"l":309.31,"c":309.31,"v":223},
{"s":"AAPL","i":"1m","t":1787701680000,"o":309.31,"h":309.36,"l":309.29,"c":309.36,"v":418}
]
The market parameter takes us or eu, and symbol takes the same form you would subscribe with. Only minutes in which the symbol actually traded produce a bar, so for a less active name the list is sparse rather than one bar per consecutive minute. Forex and crypto have no minute bars and return an empty array, as does an unknown market or symbol — this endpoint reports an empty result rather than an error for those.
Errors and status codes
Error frames come in two shapes, and the difference matters if you write one parser for both. An authentication failure uses the key status. Every other frame uses status_code.
Authentication, before the connection opens
{"status":422,"message":"Server error"} // api_token missing
{"status":403,"message":"Server error"} // token not recognised, or no real-time entitlement
These are returned as the HTTP response to the handshake, so the WebSocket never opens and nothing arrives on the socket. Browsers deliberately hide the details of a failed WebSocket handshake, so a browser client sees only a generic connection error — to tell a missing token from an unentitled one, read the HTTP status from a server-side client.
After the connection is open
| Code | Message | Cause |
|---|---|---|
| 404 | No market ‘…’ found | The endpoint path is not one of the markets listed above. |
| 422 | Invalid request | The command was not valid JSON. |
| 422 | action and symbols should be passed in a command | One of the two required keys is missing. |
| 422 | Action and symbols should be string | Wrong types — most often symbols sent as a JSON array instead of a comma-separated string. |
| 422 | Unknown action. Possible actions: subscribe, unsubscribe | The action value is not recognised. |
| 422 | Symbols limit reached | The subscription would exceed your plan’s symbol limit. |
| 422 | Only limited symbols allowed for demo | A demo token asked for a symbol outside the demo list. |
| 429 | Too many connections (token) | The 64-connection budget for this token is full. |
| 500 | Internal error. Try again later | Server-side failure. |
Examples (copy‑paste ready)
Open connection for trades:
wss://ws.eodhistoricaldata.com/ws/us?api_token=demo
Then send:
{"action": "subscribe", "symbols": "AMZN,TSLA"}
Open connection for Forex pairs:
wss://ws.eodhistoricaldata.com/ws/forex?api_token=demo
Then send:
{"action": "subscribe", "symbols": "EURUSD,AUDUSD"}
Open connection for Crypto:
wss://ws.eodhistoricaldata.com/ws/crypto?api_token=demo
Then send:
{"action": "subscribe", "symbols": "ETH-USD,BTC-USD"}
Tools for Testing
Chrome: Simple WebSocket Client extension (open a socket, paste open URL, send JSON quote).
EODHD Chrome extension: free mini ticker window for live Stocks/FX/Crypto. Perfect to see our real-time data feed. No commands needed. Install from here.
Postman (or Insomnia) – point-and-click:
- New → WebSocket Request
- URL: wss://ws.eodhistoricaldata.com/ws/crypto?api_token=demo
- Connect → send: {“action”:”subscribe”,”symbols”:”BTC-USD”}
Python (minimal script):
import asyncio, websockets, json
URL = "wss://ws.eodhistoricaldata.com/ws/crypto?api_token=demo"
async def main():
async with websockets.connect(URL) as ws:
await ws.send(json.dumps({"action":"subscribe","symbols":"BTC-USD"}))
while True:
print(await ws.recv())
asyncio.run(main())
Mac Os Terminal – websocat (via Homebrew):
Install websocat:
brew install websocat
One-liner command:
printf '{"action":"subscribe","symbols":"BTC-USD"}\n' | \
websocat "wss://ws.eodhistoricaldata.com/ws/crypto?api_token=demo"
Quick Comparison with Live (Delayed) API and Intraday API
| Aspect | Real-Time (WebSockets) | Live (Delayed) | Intraday Historical |
|---|---|---|---|
| Transport | WebSocket (push) | HTTPS REST (pull) | HTTPS REST (pull) |
| Latency / Freshness | ~live (<50 ms transport) | Stocks: 15–20 min delay; Currencies: ~1 min | Finalized ~2–3 h after US after-hours close |
| Data types | Trades & quotes; FX ticks; crypto ticks | Latest OHLCV snapshot (1-min updates) | OHLCV bars at 1m / 5m / 1h |
| Time ranges | n/a (streaming) | n/a (snapshot feed) | 1m: 120 d · 5m: 600 d · 1h: 7200 d |
| Markets & assets* | US stocks (pre/post supported), Forex & Digital Currencies | US & Global Stocks, Forex & Digital Currencies | US & Global Stocks, Forex & Digital Currencies |
| Best for | Dashboards, signals, market-making tools | Quote tickers, watchlists, lightweight UIs | Backtests, analytics, charting |
* Get the full list of covered tickers.