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

# Streaming Quotes

> Real-time quote updates using Server-Sent Events (SSE)

LeoKit's streaming quotes endpoint uses Server-Sent Events (SSE) to deliver real-time quote updates as they arrive from different protocols. This provides a better user experience than waiting for all quotes to resolve before displaying results.

## Why Use Streaming?

<CardGroup cols="2">
  <Card title="Faster Time-to-First-Quote" icon="gauge-high">
    Display the first quote in \~1-2 seconds instead of waiting 5-10 seconds for all protocols
  </Card>

  <Card title="Progressive UI Updates" icon="chart-line">
    Update your UI as each quote arrives, showing users better rates in real-time
  </Card>

  <Card title="Better UX" icon="face-smile">
    Users see immediate feedback instead of loading spinners
  </Card>

  <Card title="Lower Latency" icon="clock">
    Reduce perceived wait time with incremental updates
  </Card>
</CardGroup>

## How It Works

1. Client opens SSE connection with quote parameters
2. Server immediately responds with `init` event containing quote\_id
3. Server sends `quote` events as each protocol responds
4. Server sends `final` event with all quotes aggregated
5. Server sends `finished` event and closes connection

## Implementation

### Basic Example

```javascript theme={null}
const eventSource = new EventSource(
  "https://api.leodex.io/leokit/streaming-quotes?" +
    new URLSearchParams({
      from_asset: "BTC.BTC",
      to_asset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      amount: "100000000",
      destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      api_key: "your_api_key_here",
    })
);

let quoteId = null;

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);

  switch (data.type) {
    case "init":
      quoteId = data.quote_id;
      console.log("Waiting for", data.total, "quotes...");
      break;

    case "quote":
      console.log("Received quote from", data.protocol);
      console.log("Expected output:", data.data.expected_amount_out);
      // Update UI with quote
      break;

    case "final":
      console.log("All quotes received:", data.quotes.length);
      console.log(
        "Best quote:",
        data.quotes.find((q) => q.data.flags?.includes("OPTIMAL"))
      );
      break;

    case "finished":
      eventSource.close();
      console.log("Stream completed");
      break;

    case "error":
      console.error("Error:", data.data);
      eventSource.close();
      break;
  }
};

eventSource.onerror = (error) => {
  console.error("SSE error:", error);
  eventSource.close();
};
```

## Event Types

### 1. `init` Event

Sent immediately when the connection opens. Contains the quote\_id you'll use for the swap.

```json theme={null}
{
  "type": "init",
  "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890",
  "total": 3,
  "timestamp": "2026-01-15T12:34:56.789Z"
}
```

**Fields:**

* `quote_id` - UUID to use for deposit/status endpoints
* `total` - Expected number of quotes (one per protocol)
* `timestamp` - When the quote request was received

### 2. `quote` Event

Sent each time a protocol returns a quote. May arrive in any order.

```json theme={null}
{
  "type": "quote",
  "protocol": "thorchain",
  "data": {
    "expected_amount_out": "9850000000",
    "total_swap_seconds": 180,
    "fees": {
      "network": [
        {
          "asset": "BTC.BTC",
          "amount": "10000",
          "type": "network"
        }
      ],
      "affiliate": [],
      "liquidity": "150000000"
    },
    "route": ["BTC.BTC", "THOR.RUNE", "ETH.USDC-0xA0b86991..."],
    "flags": ["OPTIMAL", "CHEAPEST"]
  },
  "timestamp": "2026-01-15T12:34:57.123Z"
}
```

**Key Fields:**

* `protocol` - Which protocol provided this quote
* `data.expected_amount_out` - Output amount (in base units)
* `data.flags` - Array of flags like `["OPTIMAL", "FASTEST"]`

### 3. `final` Event

Sent after all protocols have responded. Contains complete array of quotes sorted by best rate.

```json theme={null}
{
  "type": "final",
  "quotes": [
    {
      "protocol": "thorchain",
      "data": { ... }
    },
    {
      "protocol": "mayaprotocol",
      "data": { ... }
    }
  ],
  "quote_id": "01936b4a-7c8e-7890-abcd-ef1234567890",
  "timestamp": "2026-01-15T12:34:59.456Z"
}
```

### 4. `finished` Event

Signals the stream is complete. Close the connection after receiving this.

```json theme={null}
{
  "type": "finished"
}
```

### 5. `error` Event

Sent if an error occurs during quote generation.

```json theme={null}
{
  "type": "error",
  "data": {
    "code": "INSUFFICIENT_LIQUIDITY",
    "message": "No protocols can handle this swap amount",
    "status": 400
  }
}
```

## React Integration

Here's a React hook for streaming quotes:

```javascript theme={null}
import { useState, useEffect } from 'react';

function useStreamingQuotes(params) {
  const [quotes, setQuotes] = useState([]);
  const [quoteId, setQuoteId] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const eventSource = new EventSource(
      `https://api.leodex.io/leokit/streaming-quotes?${new URLSearchParams({
        ...params,
        api_key: 'your_api_key_here'
      })}`
    );

    eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);

      switch (data.type) {
        case 'init':
          setQuoteId(data.quote_id);
          break;

        case 'quote':
          // Add quote to array incrementally
          setQuotes(prev => [...prev, data]);
          break;

        case 'final':
          // Replace with sorted final array
          setQuotes(data.quotes);
          setLoading(false);
          break;

        case 'finished':
          eventSource.close();
          break;

        case 'error':
          setError(data.data);
          setLoading(false);
          eventSource.close();
          break;
      }
    };

    eventSource.onerror = () => {
      setError({ message: 'Connection failed' });
      setLoading(false);
      eventSource.close();
    };

    return () => eventSource.close();
  }, [params]);

  return { quotes, quoteId, loading, error };
}

// Usage in component
function SwapWidget() {
  const { quotes, quoteId, loading } = useStreamingQuotes({
    from_asset: 'BTC.BTC',
    to_asset: 'ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
    amount: '100000000',
    destination: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  });

  return (
    <div>
      {loading && <p>Loading quotes...</p>}
      {quotes.map(quote => (
        <QuoteCard key={quote.protocol} quote={quote} />
      ))}
    </div>
  );
}
```

## Handling Disconnects

SSE connections can drop due to network issues. Implement automatic reconnection:

```javascript theme={null}
function createReconnectingEventSource(url, maxRetries = 3) {
  let retries = 0;
  let eventSource = null;

  function connect() {
    eventSource = new EventSource(url);

    eventSource.onmessage = (event) => {
      retries = 0; // Reset on successful message
      handleMessage(event);
    };

    eventSource.onerror = () => {
      eventSource.close();

      if (retries < maxRetries) {
        retries++;
        console.log(`Reconnecting... (attempt ${retries})`);
        setTimeout(connect, 1000 * retries); // Exponential backoff
      } else {
        console.error('Max retries reached');
      }
    };
  }

  connect();

  return {
    close: () => eventSource?.close()
  };
}
```

## Best Practices

<Check>
  **Always close connections** - Call `eventSource.close()` when done to prevent memory leaks.
</Check>

<Check>
  **Handle all event types** - Don't assume you'll only receive `quote` events. Handle `error` and `finished` too.
</Check>

<Check>
  **Save quote\_id from init** - You need this for the deposit endpoint. Don't wait for the `final` event.
</Check>

<Check>
  **Show incremental updates** - Display quotes as they arrive for better UX. Don't wait for `final`.
</Check>

<Check>
  **Implement timeouts** - Close the connection if no events arrive within 30 seconds.
</Check>

## SSE vs Regular Quote Endpoint

| Feature       | Streaming (`/streaming-quotes`) | Regular (`/quote`)   |
| ------------- | ------------------------------- | -------------------- |
| Response time | 1-2s for first quote            | 5-10s for all quotes |
| Updates       | Incremental                     | Single response      |
| Connection    | Persistent (SSE)                | One-time HTTP        |
| Use case      | Interactive UIs                 | Batch processing     |

## Browser Compatibility

EventSource is supported in all modern browsers:

* ✅ Chrome 6+
* ✅ Firefox 6+
* ✅ Safari 5+
* ✅ Edge 79+
* ❌ IE 11 (use polyfill)

For IE11 support, use the [EventSource polyfill](https://github.com/Yaffle/EventSource):

```javascript theme={null}
import 'event-source-polyfill';
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Connection immediately closes">
    **Cause:** Invalid API key or malformed parameters

    **Solution:** Check the `error` event for details. Verify API key is correct.
  </Accordion>

  <Accordion title="No quotes received">
    **Cause:** No protocols support the requested swap route

    **Solution:** Check `/leokit/assets` for supported assets. Verify asset identifiers.
  </Accordion>

  <Accordion title="Stream hangs after init">
    **Cause:** Network issue or server timeout

    **Solution:** Implement client-side timeout (30s recommended). Close and retry.
  </Accordion>

  <Accordion title="Quotes out of order">
    **Cause:** Protocols respond at different speeds

    **Solution:** This is normal. Sort quotes by `expected_amount_out` in your UI.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols="2">
  <Card title="Complete Swap Flow" icon="arrow-right-arrow-left" href="/guides/complete-swap-flow">
    Use the quote\_id from streaming to execute a swap
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/quote/streaming-quotes">
    Full API documentation for streaming quotes
  </Card>
</CardGroup>
