> ## Documentation Index
> Fetch the complete documentation index at: https://api-portal.etoro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Example code

> Example code for the eToro WebSocket API

The API is **one connection plus messages over it**. A topic is a string inside
a `Subscribe` message, not a URL path — you always connect to `wss://ws.etoro.com/ws`
and then subscribe.

<Note>
  Every message must be sent **after** the socket opens. A `send()` call made
  straight after `new WebSocket(...)` throws `InvalidStateError`, because the
  connection is still in the `CONNECTING` state — hence the `onopen` handler below.
</Note>

## Connect and subscribe

```javascript theme={null}
const ws = new WebSocket("wss://ws.etoro.com/ws");

// `crypto.randomUUID()` is available in all modern browsers and in Node 19+.
// The server echoes the id back, which is how a reply is matched to its request.
const newId = () => crypto.randomUUID();

const send = (payload) => ws.send(JSON.stringify(payload));

ws.onopen = () => {
    // Authenticate FIRST if you need private topics. Public topics such as
    // `instrument:<id>` do not require it.
    send({
        id: newId(),
        operation: "Authenticate",
        data: {
            userKey: "<your user key>",
            apiKey: "<your API key>"
        }
    });
};

ws.onmessage = (event) => {
    const message = JSON.parse(event.data);

    // A reply to one of your requests carries `operation` and `success`.
    // A data push carries `messages`.
    if (message.operation === "Authenticate") {
        if (!message.success) {
            console.error("Auth failed:", message.errorCode, message.errorMessage);
            return;
        }
        // Only subscribe to private topics once Authenticate is acknowledged:
        // a Subscribe that arrives first is rejected.
        send({
            id: newId(),
            operation: "Subscribe",
            data: {
                topics: ["instrument:100000", "private"],
                snapshot: true
            }
        });
        return;
    }

    if (message.messages) {
        for (const m of message.messages) {
            // `content` is a JSON-encoded STRING, so it needs a second parse.
            console.log(m.topic, m.type, JSON.parse(m.content));
        }
    }
};

ws.onerror = (event) => console.error("WebSocket error:", event);
ws.onclose = (event) => console.log("Closed:", event.code, event.reason);
```

## Unsubscribe

```javascript theme={null}
const topic = "instrument:100000";

send({
    id: newId(),
    operation: "Unsubscribe",
    data: {
        topics: [topic]
    }
});
```

## Close the connection

```javascript theme={null}
// 1000 is the WebSocket "normal closure" code.
ws.close(1000, "Done");
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.