We use cookies to improve your experience. By using this site, you agree to our use of cookies. Privacy Policy (opens in new tab)
Connect to eToro's WebSocket API for live price streaming, handle reconnections, and process real-time market data efficiently.
The eToro WebSocket API provides real-time streaming of market data including price quotes, order book updates, and trade notifications. This guide walks through connecting, subscribing to channels, and handling data efficiently.
const WebSocket = require("ws");
const WS_URL = "wss://public-api.etoro.com/stream/v1";
function connect(apiKey, userKey) {
const ws = new WebSocket(WS_URL, {
headers: {
"x-api-key": apiKey,
"x-user-key": userKey,
},
});
ws.on("open", () => {
console.log("Connected to eToro WebSocket");
});
ws.on("message", (data) => {
const message = JSON.parse(data);
handleMessage(message);
});
ws.on("close", (code, reason) => {
console.log(`Disconnected: ${code} - ${reason}`);
if (code !== 1000) {
setTimeout(() => connect(apiKey, userKey), 5000);
}
});
ws.on("error", (error) => {
console.error("WebSocket error:", error.message);
});
return ws;
}
Once connected, subscribe to specific instrument channels:
function subscribe(ws, instruments) {
ws.send(
JSON.stringify({
action: "subscribe",
channels: ["quotes"],
instruments: instruments,
})
);
}
// Subscribe to Apple, Tesla, and Bitcoin
subscribe(ws, ["AAPL", "TSLA", "BTC"]);
| Channel | Description | Update Frequency |
|---|---|---|
quotes |
Bid/ask prices | Every tick |
candles |
OHLCV candles | Per interval |
orderbook |
Level 2 depth | Every change |
trades |
Executed trades | Per trade |
function handleMessage(message) {
switch (message.type) {
case "quote":
console.log(
`${message.instrument}: Bid ${message.bid} / Ask ${message.ask}`
);
break;
case "candle":
console.log(
`${message.instrument} ${message.interval}: O${message.open} H${message.high} L${message.low} C${message.close}`
);
break;
case "heartbeat":
// Connection keepalive — no action needed
break;
default:
console.log("Unknown message type:", message.type);
}
}
Production applications need robust reconnection logic with exponential backoff:
class ReconnectingSocket {
constructor(url, apiKey, userKey) {
this.url = url;
this.apiKey = apiKey;
this.userKey = userKey;
this.attempt = 0;
this.maxDelay = 30000;
this.subscriptions = [];
this.connect();
}
connect() {
this.ws = new WebSocket(this.url, {
headers: {
"x-api-key": this.apiKey,
"x-user-key": this.userKey,
},
});
this.ws.on("open", () => {
this.attempt = 0;
this.resubscribe();
});
this.ws.on("close", (code) => {
if (code !== 1000) {
const delay = Math.min(
1000 * Math.pow(2, this.attempt),
this.maxDelay
);
this.attempt++;
console.log(`Reconnecting in ${delay}ms (attempt ${this.attempt})`);
setTimeout(() => this.connect(), delay);
}
});
}
subscribe(channels, instruments) {
this.subscriptions.push({ channels, instruments });
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(
JSON.stringify({ action: "subscribe", channels, instruments })
);
}
}
resubscribe() {
for (const sub of this.subscriptions) {
this.ws.send(
JSON.stringify({
action: "subscribe",
channels: sub.channels,
instruments: sub.instruments,
})
);
}
}
}
requestAnimationFrame or debounce for rendering