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

# Stream Top-of-Book Quotes

> Connect to the quote stream and read live bid/ask updates for a token pair

<Info>
  During development, the [developer endpoints](/get-started/endpoints) work without an API key. Access to the quote stream in production is gated: [request an API key](/get-started/api-key) with quote stream access, or have an existing key upgraded.
</Info>

This recipe connects to the [quote stream](/resources/trading-api/websockets/quote-stream) and reads live bid and ask updates for a single pair. See [Market Data Streams](/spot/trading/market-data-streams) for how the quotes are built.

<Steps>
  <Step title="Set up">
    Import a WebSocket client, read the endpoint from the environment, and pick the pair to watch.

    <AccordionGroup>
      <Accordion title="Imports and config">
        ```typescript theme={null}
        import "dotenv/config";
        import WebSocket from "ws";

        const DFLOW_TRADE_API_WS_URL =
          process.env.DFLOW_TRADE_API_WS_URL ?? "wss://dev-quote-api.dflow.net";
        const DFLOW_API_KEY = process.env.DFLOW_API_KEY; // optional; not needed for dev

        const SOL_MINT = "So11111111111111111111111111111111111111112";
        const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
        ```
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Connect and subscribe">
    Open the connection to `/quote-stream` and send a `subscribe` op once it is open. The API key header is only needed in production.

    <AccordionGroup>
      <Accordion title="Connect and subscribe">
        ```typescript theme={null}
        const ws = new WebSocket(
          `${DFLOW_TRADE_API_WS_URL}/quote-stream`,
          DFLOW_API_KEY ? { headers: { "x-api-key": DFLOW_API_KEY } } : undefined
        );

        ws.on("open", () => {
          ws.send(
            JSON.stringify({ op: "subscribe", base_mint: SOL_MINT, quote_mint: USDC_MINT })
          );
        });
        ```
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Read updates">
    Each message is a batch covering one slot. Read the `updates` array, and handle the per-pair `e` field for pairs with no route.

    <AccordionGroup>
      <Accordion title="Handle messages">
        ```typescript theme={null}
        ws.on("message", (data) => {
          const frame = JSON.parse(data.toString());
          for (const u of frame.updates ?? []) {
            if (u.e) {
              console.log(`${u.sb} / ${u.sq}: ${u.e}`);
              continue;
            }
            console.log(`slot ${frame.u}  bid ${u.b}  ask ${u.a}`);
          }
        });

        ws.on("error", (err) => console.error("WebSocket error:", err));
        ws.on("close", () => console.log("Connection closed"));

        // demo only: stop after a few seconds
        setTimeout(() => ws.close(), 4000);
        ```
      </Accordion>
    </AccordionGroup>
  </Step>
</Steps>

<Note>
  Quotes are approximations and can briefly cross (bid above ask) between slots.
  Treat them as a live read on the market, not a guaranteed execution price.
</Note>

## Related Resources

<CardGroup cols={2}>
  <Card title="Market Data Streams" href="/spot/trading/market-data-streams" icon="signal-stream">
    How the streams work and when to use each.
  </Card>

  <Card title="Quote Stream API" href="/resources/trading-api/websockets/quote-stream" icon="plug">
    Messages, fields, and the all-markets subscription.
  </Card>

  <Card title="Stream Order Book Depth" href="/spot/recipes/stream-order-book" icon="layer-group">
    Read ten levels of depth per side.
  </Card>

  <Card title="Authenticate Requests" href="/resources/recipes/api-keys" icon="key">
    Authenticate requests for production rate limits.
  </Card>
</CardGroup>
