# The WebSocket stream

Source: https://docs.pipsocket.com/websocket

Fills, deals, prices and the account as they happen, and calls over the same socket.

Each account has a stream: a WebSocket that pushes what happens on the account as it happens, and takes the API's calls as messages.

```text
wss://wss.pipsocket.com/v1/accounts/{account}/stream
Authorization: Bearer $PIPSOCKET_KEY
```

The key goes in the `Authorization` header of the upgrade request, as on REST, so connect from your server (browsers can't set that header). It is checked when the stream connects, not on each call: see [rolling the key](/authentication#rolling-the-key). Before the upgrade, a missing or wrong key answers `401` and an account your workspace doesn't have `404`, as on REST; a request that isn't a WebSocket upgrade answers `426 upgrade_required`.

<Tabs items={["Node", "Python"]}>
<Tab value="Node">

```js
// npm install ws
import WebSocket from "ws"

const socket = new WebSocket("wss://wss.pipsocket.com/v1/accounts/acc_01m6tx3k9f2qw8r7bznd5yvh4c/stream", {
  headers: { Authorization: `Bearer ${process.env.PIPSOCKET_KEY}` },
})

socket.on("open", () => {
  socket.send(JSON.stringify({ id: "t1", method: "PUT", path: "/ticks", body: { symbols: ["EURUSD"] } }))
})
socket.on("message", (data) => console.log(JSON.parse(data.toString())))
```

</Tab>
<Tab value="Python">

```python
# pip install websockets
import asyncio, json, os

import websockets


async def main():
    url = "wss://wss.pipsocket.com/v1/accounts/acc_01m6tx3k9f2qw8r7bznd5yvh4c/stream"
    headers = {"Authorization": f"Bearer {os.environ['PIPSOCKET_KEY']}"}
    async with websockets.connect(url, additional_headers=headers) as socket:
        await socket.send(json.dumps({"id": "t1", "method": "PUT", "path": "/ticks", "body": {"symbols": ["EURUSD"]}}))
        async for message in socket:
            print(json.loads(message))


asyncio.run(main())
```

</Tab>
</Tabs>

The first message is `{ "type": "subscribed", "account": "acc_..." }`. Every message is a JSON object with a `type`.

## Events

| `type` | When | Fields |
| --- | --- | --- |
| `request` | A trade request settled (filled, rejected, refused), however it was sent: REST, this stream, or the kill switch. | As [`GET /requests/{request}`](/api/look-up-a-request) shows it: `request`, `state`, `sent`, `result`, `message`. |
| `deal` | A deal on the account: a fill, a close, a stop loss or take profit, a balance operation. | `deal`, as in [deal history](/api/history). |
| `transaction` | MT5's trade transaction (`OnTradeTransaction`): orders and positions changing. | `transaction` (MT5's type, such as `deal_add` or `position`), `order`, `deal`, `position`, `position_by`, `symbol`, `order_type`, `order_state`, `deal_type`, `volume`, `price`, `sl`, `tp`. |
| `account` | Every second. | `connected`, `trade_allowed`, `balance`, `equity`, `credit`, `margin`, `margin_free`, `positions`, `orders`, `server_time`. |
| `status` | The account's status changed, the terminal connecting and disconnecting included. | `status`, `detail` (see [Accounts and ids](/accounts#accounts)). |
| `tick` | A price change of a symbol you asked for (see Ticks). | `symbol`, `time_msc`, `bid`, `ask`, `last`, `volume`, `flags` (MT5's `MqlTick`). |
| `lagged` | The stream fell behind and dropped events. | `missed`: how many. Read the state over REST. |
| `engine_disconnected` | The account's host lost its connection to Pipsocket. | None. See Disconnections. |

Your own fill arrives as a `request` event whichever way you sent the order, so a client can send over REST and listen here.

## Calls

Send any API call as a message, with `path` as under `/v1/accounts/{account}`:

```json
{ "id": "o1", "method": "POST", "path": "/orders", "body": { "symbol": "EURUSD", "type": "buy", "volume": 0.01, "type_filling": "ioc" }, "idempotency_key": "rebalance-0412" }
```

- `id` is your own string, and comes back on the answer: `{ "type": "response", "id": "o1", "status": 200, "body": {...} }`. `status` and `body` are exactly what the REST call answers.
- `idempotency_key` is the `Idempotency-Key` header. `path` may carry a query: `/history/deals?from=1794019000&to=1794021000`. The account itself is `/`.
- A call runs through the same checks, kill switch and idempotency as REST. Calls run side by side, so a slow one doesn't hold up the next.
- At most 16 calls can wait for their answer on one stream. Another is refused: `{ "type": "error", "id": "o2", "error": "too_many_calls", "message": "..." }`.
- A message that isn't a call (no string `id`, `method` or `path`) is refused with `invalid_message`, and a call while the host is away with `engine_unavailable`. The stream stays open.

## Ticks

Ask for prices with a call to `/ticks`, kept by the stream itself:

```json
{ "id": "t1", "method": "PUT", "path": "/ticks", "body": { "symbols": ["EURUSD", "XAUUSD"] } }
```

- Send all the symbols you want each time, up to 100; `[]` stops. The answer is the stream's symbols: `{ "type": "response", "id": "t1", "status": 200, "body": { "symbols": ["EURUSD", "XAUUSD"] } }`. `GET /ticks` reads them back.
- A `tick` event follows for each symbol's current price, then one for each price change as the broker sent it. Of changes closer together than 10 ms, only the latest is sent.
- An account's streams share the 100 symbols between them: a set that would pass that is `422 too_many_symbols`. A list that isn't up to 100 symbol names is `422 invalid_request`, and a method other than `GET` or `PUT` is `405 method_not_allowed`.

## Keeping it open

Send the text message `ping` and the stream answers `pong`, without a call: use it to keep idle connections (and proxies between you) from timing out.

## Disconnections

- **The host goes away.** You get `engine_disconnected`, and calls still waiting get no answer. A trade among them may or may not have reached the host. Send it again with the same `idempotency_key` once the host is back: you get the first request if the host recorded it (it settles all the same), and the trade is sent if it never arrived. A trade sent without a key can't be retried safely: check positions and history first. Your stream stays open; events and ticks resume when the host reconnects.
- **Your socket closes.** Connect again and send your `/ticks` set again: it belonged to the closed stream. Events that happened meanwhile aren't sent again, so read what you need over REST: [positions](/api/positions), [pending orders](/api/pending-orders), [history](/api/history), and [requests](/api/look-up-a-request) you were waiting for.
- **`lagged`.** The stream dropped events: read the state over REST, as above.

