# Streaming Real-Time Data

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

Sends [normalized](/tardis-machine/data-types.md) real-time market data for [data types](#stream-normalized-options) specified via query string. See [supported data types](/tardis-machine/data-types.md) which include normalized [trade](/tardis-machine/data-types.md#trade), [order book change](/tardis-machine/data-types.md#book_change), [customizable order book snapshots](/tardis-machine/data-types.md#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](/tardis-machine/replaying-historical-data.md#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](/tardis-machine/data-types.md). 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](/node-client/quickstart.md) 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](/tardis-machine/data-types.md).
   {% 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](/api/http-api-reference.md#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](/tardis-machine/data-types.md) for which real-time data will be provided                                                                                                                                |
| **withDisconnectMessages** | boolean (optional)   | undefined | when set to `true`, sends [disconnect](/tardis-machine/data-types.md#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](/tardis-machine/data-types.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tardis.dev/tardis-machine/streaming-real-time-data.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
