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

# Streaming quotes (WebSocket)

> Push quoting over one socket — re-quotes fire when the pools under your route move, not on a timer

## Overview

`wss://router.raze.bot/swap/sol/quote/stream` keeps a quote fresh for you. Open one
socket, `subscribe` to a pair and an amount, and the router pushes a new quote
whenever on-chain state under the **current route** moves — no polling loop.

Re-quotes are **event-driven**: the ingest taps wake a subscription the moment a
transaction touches one of its route's pools, so a push lands right after the fill
that moved the price. A slower fallback re-quote still runs underneath so a route can
*shift* onto pools the watch set doesn't cover yet (a fresh pool, a warming index).

|              |                                                                                                                      |
| ------------ | -------------------------------------------------------------------------------------------------------------------- |
| **Endpoint** | `wss://router.raze.bot/swap/sol/quote/stream`                                                                        |
| **Auth**     | `?apiKey=sk_...` query param, or `Authorization: Bearer sk_...` / `X-API-Key` headers where your client can set them |
| **Frames**   | JSON text                                                                                                            |
| **Payload**  | `data` is byte-for-byte the `data` object of [`GET /swap/sol/quote/{mint}`](/api-reference/router/quote)             |

<Note>
  Because `data` is the identical shape, a front-end can reuse its REST parsing
  verbatim — including `routes[]`, `splitLegs[]` and `routeTicket`, which is
  redeemable at `/swap/sol/instructions` and `/swap/sol/buy` exactly like a polled one.
</Note>

An unauthenticated upgrade is rejected with **401** (`code: "AUTH_REQUIRED"`) before
the socket opens.

## Client frames

### `subscribe`

```json theme={null}
{
  "op": "subscribe",
  "id": "s1",
  "mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
  "amount": 1000000000,
  "slippageBps": 500,
  "swapMode": "exactIn",
  "maxHops": 3,
  "includeDex": "pumpswap,raydium-cpmm",
  "excludeDex": "",
  "minIntervalMs": 150
}
```

| Field                                 | Required | Notes                                                                                                                                                                                                                             |
| ------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                  | yes      | Your handle for this subscription; echoed on every frame.                                                                                                                                                                         |
| `mint`                                | yes      | Same role as the `:mint` path segment of `GET /quote`.                                                                                                                                                                            |
| `amount`                              | yes      | Raw input amount. `0` is rejected.                                                                                                                                                                                                |
| `inputMint` / `outputMint`            | no       | Resolve sides **exactly** like `GET /quote`: both together are honoured verbatim (cross token→token); `inputMint` alone that is a quote mint buys `mint`; `outputMint` alone sells `mint` into it; neither = buy `mint` with SOL. |
| `slippageBps`                         | no       | Default 500.                                                                                                                                                                                                                      |
| `swapMode`                            | no       | `exactIn` (default) or `exactOut`.                                                                                                                                                                                                |
| `maxHops`, `includeDex`, `excludeDex` | no       | Same routing constraints as REST. CSV strings here.                                                                                                                                                                               |
| `minIntervalMs`                       | no       | Your pacing floor. Raised to the server floor (150 ms) when lower, capped at 5000 ms.                                                                                                                                             |

Re-sending `subscribe` with a **live `id`** replaces that subscription atomically —
this is how you edit an amount without a gap.

### `unsubscribe` / `ping`

```json theme={null}
{"op": "unsubscribe", "id": "s1"}
{"op": "ping"}
```

## Server frames

| Frame          | Shape                                                                                                                                    |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `subscribed`   | `{"op":"subscribed","id":"s1"}`                                                                                                          |
| `unsubscribed` | `{"op":"unsubscribed","id":"s1"}`                                                                                                        |
| `quote`        | `{"op":"quote","id":"s1","seq":3,"slot":354812901,"data":{ …GET /quote shape… }}`                                                        |
| `no_route`     | `{"op":"no_route","id":"s1","seq":4,"slot":354812903}` — nothing routable right now; the subscription stays live and recovers on its own |
| `pong`         | `{"op":"pong"}`                                                                                                                          |
| `error`        | `{"op":"error","id":"s1","message":"…"}`                                                                                                 |

`seq` increments per subscription, so you can drop out-of-order or duplicated frames
without tracking timestamps.

### Push semantics

* Pushes are **deduped on a route fingerprint** (amounts + pools + hop set). An
  unchanged quote is re-sent only on the \~5 s heartbeat — so a repeat frame means
  "fresh and flat", and silence past the heartbeat means the stream is stalled, not
  that the price stopped moving.
* The fallback re-quote runs about every 400 ms (≈ slot cadence) even with no wake.
* A `no_route` is likewise emitted once on transition, then only on the heartbeat.

### Errors

| Message                                     | Cause                                                                           |
| ------------------------------------------- | ------------------------------------------------------------------------------- |
| `bad message: …`                            | The frame didn't parse as one of the client ops.                                |
| `amount required`                           | `amount` was `0` or missing.                                                    |
| `too many subscriptions on this connection` | Per-connection cap (16 by default) — open a second socket or unsubscribe first. |
| `server at streaming capacity`              | Global subscription budget is full; retry with backoff.                         |

## Example

```js theme={null}
const ws = new WebSocket(
  "wss://router.raze.bot/swap/sol/quote/stream?apiKey=sk_your_api_key"
);

ws.onopen = () => {
  ws.send(JSON.stringify({
    op: "subscribe",
    id: "sol-bonk",
    mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
    amount: 1_000_000_000,   // 1 SOL in
    slippageBps: 500,
  }));
};

ws.onmessage = (ev) => {
  const msg = JSON.parse(ev.data);
  switch (msg.op) {
    case "quote":
      // msg.data is identical to GET /swap/sol/quote/{mint} → data
      console.log(msg.seq, msg.data.amountOut, msg.data.platform, msg.data.priceImpactBps);
      break;
    case "no_route":
      console.warn("no route at slot", msg.slot);
      break;
    case "error":
      console.error(msg.message);
      break;
  }
};

// Edit the size in place — same id replaces the subscription atomically.
function resize(lamports) {
  ws.send(JSON.stringify({
    op: "subscribe",
    id: "sol-bonk",
    mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
    amount: lamports,
  }));
}
```
