# Streaming Real-Time Data

## `WebSocket` /ws-stream-normalized?options={options}

Sends [normalized](https://docs.tardis.dev/tardis-machine/data-types) real-time market data for [data types](#stream-normalized-options) specified via query string. See [supported data types](https://docs.tardis.dev/tardis-machine/data-types) which include normalized [trade](https://docs.tardis.dev/data-types#trade), [order book change](https://docs.tardis.dev/data-types#book_change), [customizable order book snapshots](https://docs.tardis.dev/data-types#book_snapshot_-number_of_levels-_-snapshot_interval-time_unit) etc.

**Doesn't require an API key, as it connects directly to exchanges' real-time WebSocket APIs** and transparently restarts closed, broken, or stale connections (open connections without data being sent for a specified amount of time).

Provides **consolidated real-time market data streaming** functionality with [options](#stream-normalized-options) as an array - provides single consolidated real-time data stream for all exchanges specified in [options array](#stream-normalized-options).

{% hint style="info" %}
[WebSocket /ws-replay-normalized](https://docs.tardis.dev/replaying-historical-data#websocket-ws-replay-normalized-options-options) is the historical counterpart of this API endpoint, providing historical market data in the same format.
{% endhint %}

{% hint style="info" %}
Tardis-machine provides real-time streaming only for [normalized data types](https://docs.tardis.dev/tardis-machine/data-types). If you need real-time data in exchange-native format, connect directly to the exchange's WebSocket API.
{% endhint %}

{% tabs %}
{% tab title="Python" %}

```python
import asyncio
import aiohttp
import json
import urllib.parse


async def main():
    data_types = ["trade", "book_change", "book_snapshot_10_100ms"]

    stream_options = [
        {
            "exchange": "binance",
            "symbols": ["btcusdt"],
            "dataTypes": data_types,
        },
        {
            "exchange": "binance-futures",
            "symbols": ["BTCUSDT"],
            "dataTypes": data_types,
        },
    ]

    options = urllib.parse.quote_plus(json.dumps(stream_options))

    URL = f"ws://localhost:8001/ws-stream-normalized?options={options}"
    # real-time normalized data for two exchanges via single connection
    async with aiohttp.ClientSession() as session:
        async with session.ws_connect(URL) as websocket:

            async for msg in websocket:
                print(msg.data)


asyncio.run(main())
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
import WebSocket from 'ws'


const serialize = options => {
  return encodeURIComponent(JSON.stringify(options))
}
// other available data types examples:
// 'book_snapshot_10_100ms', 'derivative_ticker', 'quote',
// 'trade_bar_10ms', 'trade_bar_10s'
const dataTypes = ['trade', 'book_change', 'book_snapshot_10_100ms']

const streamOptions = [
  {
    exchange: 'binance',
    symbols: ['btcusdt'],
    dataTypes
  },
  {
    exchange: 'binance-futures',
    symbols: ['BTCUSDT'],
    dataTypes
  }
]

const options = serialize(streamOptions)
const URL = `ws://localhost:8001/ws-stream-normalized?options=${options}`
const ws = new WebSocket(URL)
// real-time normalized data for two exchanges via single connection
ws.onmessage = message => {
  console.log(message.data)
}
```

{% hint style="info" %}
See also official Tardis.dev [Node.js client](https://docs.tardis.dev/node-client/quickstart) library.
{% endhint %}
{% endtab %}

{% tab title="Your preferred language" %}
We're working on providing more samples and dedicated client libraries in different languages, but in the meanwhile to consume [WebSocket /ws-stream-normalized](#websocket-ws-stream-normalized-options-options) API responses in your language of choice, you should:

1. Provide url encoded JSON [options](#stream-normalized-options) via options query string param when connecting to

   [WebSocket /ws-stream-normalized](#websocket-ws-stream-normalized-options-options) endpoint
2. Parse each received WebSocket message as JSON containing [normalized data](https://docs.tardis.dev/tardis-machine/data-types).
   {% endtab %}
   {% endtabs %}

#### Stream normalized options

[WebSocket /ws-stream-normalized](#websocket-ws-stream-normalized-options-options) endpoint accepts required options query string param in **url encoded JSON format**.

Options JSON needs to be an object or an array of objects with fields as specified below. If array is specified then API provides single consolidated real-time data stream for all exchanges specified (as in examples above).

| name                       | type                 | default   | description                                                                                                                                                                                                                              |
| -------------------------- | -------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **exchange**               | string               | -         | requested exchange id - use [/exchanges HTTP API](https://docs.tardis.dev/api/http-api-reference#exchanges) to get list of valid exchanges ids                                                                                           |
| **symbols**                | string\[] (optional) | undefined | optional symbols of requested real-time data feed                                                                                                                                                                                        |
| **dataTypes**              | string\[]            | -         | array of normalized [data types](https://docs.tardis.dev/tardis-machine/data-types) for which real-time data will be provided                                                                                                            |
| **withDisconnectMessages** | boolean (optional)   | undefined | when set to `true`, sends [disconnect](https://docs.tardis.dev/data-types#disconnect) messages anytime underlying exchange real-time WebSocket connection(s) gets disconnected                                                           |
| **timeoutIntervalMS**      | number               | 10000     | specifies time in milliseconds after which connection to real-time exchanges' WebSocket API is restarted if no message has been received                                                                                                 |
| **withErrorMessages**      | boolean (optional)   | undefined | when set to `true`, sends `error` messages whenever an underlying exchange WebSocket connection error occurs. Error message format: `{"type":"error","exchange":"...","localTimestamp":"...","details":"...","subSequentErrorsCount":1}` |

{% hint style="info" %}
**Tips:**

* For sparse instruments (e.g., illiquid options), increase `timeoutIntervalMS` (e.g., `60000`) to prevent unnecessary connection restarts when no messages arrive for extended periods. This is especially important for `trade_bar_*` data types on low-volume symbols.
* When providing `options` as an array, each element creates a separate WebSocket connection to the target exchange. This can be used for multi-exchange streaming, but also for the same exchange — for example, to work around per-connection limits on the number of symbols or channels.
  {% endhint %}

#### **Response format & sample messages**

See [Output Data Types](https://docs.tardis.dev/tardis-machine/data-types).
