Connect to eToro's WebSocket API for live price streaming, handle reconnections, and process real-time market data efficiently.
Part of: Build a Trading Bot — Step 3 of 4
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://ws.etoro.com/ws";
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 renderingWas this helpful?
How to programmatically search, filter, and explore eToro's instrument catalog — asset classes, exchanges, industries, and historical data.
Best practices for using the eToro Watchlists API programmatically — curated lists, bulk operations, and organizational patterns.