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

# WebSocket Quote Streaming

> Stream real-time swap quotes over WebSocket with the Node SDK, covering subscriptions, message formats, and reconnection handling

## Overview

The SDK provides WebSocket support for real-time quote streaming. The WebSocket client handles connection management, automatic reconnection with exponential backoff, and ping/pong keep-alive.

There are two ways to use WebSocket:

1. **High-level**: Use `streamQuotes` / `streamQuotesByTicker` convenience methods on `MakerClient`
2. **Low-level**: Use `WSClient` directly with event-based message handling

## Enabling WebSocket

Before streaming quotes, enable the WebSocket connection:

```typescript TypeScript theme={null}
const ws = client.maker.enableWebSocket('wss://api.signet.kaleidoswap.com/api/v1/market/ws/my-client-id');
```

```python Python theme={null}
ws = client.maker.enable_websocket("wss://api.signet.kaleidoswap.com/api/v1/market/ws/my-client-id")
```

The URL includes a client ID for session tracking. The `enableWebSocket` method returns a `WSClient` instance.

> [!NOTE]
>
> The examples on this page use Signet, which is also the SDK default. Match the WebSocket URL to the environment your `baseUrl` points at. See [Available Environments](https://docs.kaleidoswap.com/cli/node-sdk/installation.md#available-environments).

## High-Level API

### `streamQuotesByTicker` / `stream_quotes_by_ticker`

The simplest way to stream quotes. Automatically discovers routes and starts streaming.

```typescript TypeScript theme={null}
const unsubscribe = await client.maker.streamQuotesByTicker(
  'BTC',           // fromTicker
  'USDT',          // toTicker
  100000,          // amount
  (quote) => {     // callback
    console.log(`Price: ${quote.price}, RFQ: ${quote.rfq_id}`);
  },
  {                // options (optional)
    preferredFromLayer: 'BTC_LN',
    preferredToLayer: 'RGB_LN',
    pollInterval: 3000
  }
);

// Stop streaming when done
unsubscribe();
```

```python Python theme={null}
from kaleido_sdk import Layer

unsubscribe = await client.maker.stream_quotes_by_ticker(
    from_ticker="BTC",
    to_ticker="USDT",
    amount=100000,
    on_update=lambda quote: print(f"Price: {quote['price']}, RFQ: {quote['rfq_id']}"),
    preferred_from_layer=Layer.BTC_LN,
    preferred_to_layer=Layer.RGB_LN,
    poll_interval=3.0,
)

# Stop streaming when done
unsubscribe()
```

Options:

| Option | Type | Description |
| - | - | - |
| `preferredFromLayer` / `preferred_from_layer` | `Layer` | Pin the source layer instead of letting route discovery pick |
| `preferredToLayer` / `preferred_to_layer` | `Layer` | Pin the destination layer |
| `pollInterval` / `poll_interval` | number | Time between quote requests. TypeScript takes milliseconds (default `2000`), Python takes seconds |

### `streamQuotes` / `stream_quotes`

Stream quotes for a specific route (tickers and layers).

```typescript TypeScript theme={null}
const unsubscribe = await client.maker.streamQuotes(
  'BTC',           // from_asset ticker
  'USDT',          // to_asset ticker
  100000,          // from_amount (or null)
  'BTC_LN',        // from_layer (or null)
  'RGB_LN',        // to_layer (or null)
  (quote) => {
    console.log(`${quote.from_asset.amount} -> ${quote.to_asset.amount}`);
  }
);
```

```python Python theme={null}
unsubscribe = await client.maker.stream_quotes(
    from_asset="BTC",
    to_asset="USDT",
    from_amount=100000,
    from_layer=Layer.BTC_LN,
    to_layer=Layer.RGB_LN,
    on_update=lambda quote: print(f"{quote['from_asset']['amount']} -> {quote['to_asset']['amount']}")
)
```

### `streamQuotesForAllRoutes` / `stream_quotes_for_all_routes`

Stream quotes for all available routes between two tickers simultaneously.

```typescript TypeScript theme={null}
const unsubscribers = await client.maker.streamQuotesForAllRoutes(
  'BTC', 'USDT', 100000,
  (route, quote) => {
    console.log(`Route ${route}: price=${quote.price}`);
  }
);
// Returns: Map<string, () => void>
// Call each unsubscriber to stop, or iterate and call all
```

```python Python theme={null}
unsubscribers = await client.maker.stream_quotes_for_all_routes(
    from_ticker="BTC",
    to_ticker="USDT",
    amount=100000,
    on_update=lambda route, quote: print(f"Route {route}: price={quote['price']}")
)
# Returns: dict[str, Callable]
```

### `getAvailableRoutes` / `get_available_routes`

Discover available routes for a pair before streaming.

```typescript TypeScript theme={null}
const routes = await client.maker.getAvailableRoutes('BTC', 'USDT');
// Returns: Array<{ from_layer: string; to_layer: string }>
for (const route of routes) {
  console.log(`${route.from_layer} -> ${route.to_layer}`);
}
```

```python Python theme={null}
routes = await client.maker.get_available_routes("BTC", "USDT")
for route in routes:
    print(f"{route['from_layer']} -> {route['to_layer']}")
```

## Low-Level WSClient API

For full control, use the `WSClient` directly.

### Connection

```typescript TypeScript theme={null}
const ws = client.maker.enableWebSocket('wss://api.signet.kaleidoswap.com/api/v1/market/ws/my-client');

await ws.connect();
console.log(`Connected: ${ws.isConnected()}`);

// When done
ws.disconnect();
```

```python Python theme={null}
ws = client.maker.enable_websocket("wss://api.signet.kaleidoswap.com/api/v1/market/ws/my-client")

await ws.connect()
print(f"Connected: {ws.is_connected()}")

# When done
ws.disconnect()
```

### Events

Subscribe to events using `on` / `off`:

| Event | Data | Description |
| - | - | - |
| `connected` | -- | Connection established |
| `disconnected` | -- | Connection closed |
| `reconnecting` | `attempt: number` | Attempting reconnection |
| `quoteResponse` / `quote_response` | `QuoteResponse` | New quote received |
| `connectionEstablished` / `connection_established` | connection data | Server confirmed connection |
| `pong` | `PongResponse` | Pong received |
| `error` | `Error` | An error occurred |
| `maxReconnectExceeded` / `max_reconnect_exceeded` | -- | Max reconnection attempts reached |

```typescript TypeScript theme={null}
ws.on('connected', () => console.log('Connected'));
ws.on('disconnected', () => console.log('Disconnected'));
ws.on('reconnecting', (attempt) => console.log(`Reconnecting: attempt ${attempt}`));
ws.on('quoteResponse', (quote) => console.log(`Quote: ${quote.price}`));
ws.on('error', (error) => console.error(`Error: ${error.message}`));
ws.on('maxReconnectExceeded', () => console.log('Max reconnects exceeded'));

// Unsubscribe
const handler = (quote) => { /* ... */ };
ws.on('quoteResponse', handler);
ws.off('quoteResponse', handler);
```

```python Python theme={null}
ws.on("connected", lambda: print("Connected"))
ws.on("disconnected", lambda: print("Disconnected"))
ws.on("reconnecting", lambda attempt: print(f"Reconnecting: attempt {attempt}"))
ws.on("quote_response", lambda quote: print(f"Quote: {quote['price']}"))
ws.on("error", lambda err: print(f"Error: {err}"))
ws.on("max_reconnect_exceeded", lambda: print("Max reconnects exceeded"))

# Unsubscribe
handler = lambda quote: ...
ws.on("quote_response", handler)
ws.off("quote_response", handler)
```

### Requesting Quotes

```typescript TypeScript theme={null}
ws.requestQuote({
  from_asset: 'BTC',
  to_asset: 'USDT',
  from_amount: 100000,
  from_layer: 'BTC_LN',
  to_layer: 'RGB_LN'
});
```

```python Python theme={null}
ws.request_quote({
    "from_asset": "BTC",
    "to_asset": "USDT",
    "from_amount": 100000,
    "from_layer": "BTC_LN",
    "to_layer": "RGB_LN"
})
```

### Ping / Keep-Alive

The WSClient automatically pings the server at the configured interval. You can also ping manually:

```typescript TypeScript theme={null}
ws.ping();
```

```python Python theme={null}
ws.ping()
```

## Configuration

The WSClient accepts configuration options:

| Option | Default | Description |
| - | - | - |
| `maxReconnectAttempts` / `max_reconnect_attempts` | `5` | Maximum reconnection attempts |
| `reconnectDelay` / `reconnect_delay` | `1000ms` / `1.0s` | Base delay between reconnections (exponential backoff) |
| `pingInterval` / `ping_interval` | `30000ms` / `30.0s` | Interval between keep-alive pings |

Reconnection uses exponential backoff: `delay * 2^attempt`.

## WebSocket Protocol

The WebSocket uses a JSON message protocol:

### Message Types

| Action | Direction | Description |
| - | - | - |
| `quote_request` | Client -> Server | Request a price quote |
| `quote_response` | Server -> Client | Quote update |
| `ping` | Client -> Server | Keep-alive ping |
| `pong` | Server -> Client | Keep-alive response |
| `connection_established` | Server -> Client | Connection confirmed |
| `error` | Server -> Client | Error message |

### QuoteResponse Fields

| Field | Type | Description |
| - | - | - |
| `action` | string | Always `quote_response` |
| `from_asset` | SwapLegData | Complete specification for the source leg of the swap |
| `to_asset` | SwapLegData | Complete specification for the destination leg of the swap |
| `price` | number | Price of 1 whole unit of `from_asset` expressed in the smallest unit of `to_asset` |
| `rfq_id` | string | Request-for-quote ID (pass to `initSwap`) |
| `timestamp` | number | Server timestamp |
| `expires_at` | number | Quote expiry timestamp |
| `fee` | Fee | Fee breakdown |

Each `SwapLegData` object describes one leg of the swap:

| Field | Type | Description |
| - | - | - |
| `asset_id` | string | Unique asset identifier (e.g. `BTC`, RGB contract ID) |
| `name` | string | Full asset name (e.g. `Bitcoin`) |
| `ticker` | string | Display ticker (e.g. `BTC`, `USDT`) |
| `layer` | string | Settlement layer (e.g. `BTC_LN`, `RGB_LN`) |
| `amount` | number | Amount in raw smallest units |
| `precision` | number | Decimal places for the asset (e.g. 8 for BTC) |

## Best Practices

> **Handle disconnections gracefully**
>
> Subscribe to `disconnected` and `reconnecting` events. The WSClient automatically reconnects with exponential backoff, but you should handle the case where max attempts are exceeded.

> **Use the high-level API when possible**
>
> `streamQuotesByTicker` handles route discovery, connection management, and quote routing for you. Only use the low-level `WSClient` when you need custom control.

> **Unsubscribe when done**
>
> Always call the unsubscribe function returned by `streamQuotes` / `streamQuotesByTicker` when you no longer need quotes. This prevents memory leaks and unnecessary network traffic.

> **Use rfq\_id from quotes to initiate swaps**
>
> The `rfq_id` in each quote response is what you pass to `initSwap` (`POST /api/v1/swaps/init`). Use the most recent quote to ensure the rate is still valid.

## Next Steps

> **[Examples](https://docs.kaleidoswap.com/cli/node-sdk/examples.md)**
>
> See WebSocket streaming in complete end-to-end examples

> **[Client Reference](https://docs.kaleidoswap.com/cli/node-sdk/api-reference.md)**
>
> Full reference for all streaming methods on MakerClient

> **[Types](https://docs.kaleidoswap.com/cli/node-sdk/types.md)**
>
> QuoteResponse, QuoteRequest, and other WebSocket type definitions

> **[Best Practices](https://docs.kaleidoswap.com/cli/node-sdk/best-practices.md)**
>
> Reconnection strategies and production patterns


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