> ## 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

> Stream quote results in real-time as each protocol responds via Server-Sent Events

## Overview

The streaming-quotes endpoint emits Server-Sent Events as each protocol returns a quote, instead of waiting for all protocols to complete. Use this for snappy UI — display the first quote in \~200ms instead of waiting 5+ seconds for the slowest aggregator.

For a hands-on walkthrough see the [Streaming Quotes guide](/guides/streaming-quotes).

## Endpoint

```
GET /leokit/streaming-quotes
```

## Request

### Authentication

EventSource cannot set custom headers reliably, so authentication accepts both styles:

| Method                    | Example                                           |
| ------------------------- | ------------------------------------------------- |
| `Api-Key` header          | `Api-Key: YOUR_API_KEY` (preferred when possible) |
| `api_key` query parameter | `?api_key=YOUR_API_KEY`                           |

### Query Parameters

| Parameter            | Type   | Required | Description                                          |
| -------------------- | ------ | -------- | ---------------------------------------------------- |
| `from_asset`         | string | Yes      | Source asset in `CHAIN.SYMBOL-ADDRESS` format        |
| `to_asset`           | string | Yes      | Destination asset in `CHAIN.SYMBOL-ADDRESS` format   |
| `amount`             | string | Yes      | Amount in source-asset base units (wei, sats, etc.)  |
| `origin`             | string | No       | Source wallet address (improves quote accuracy)      |
| `destination`        | string | No       | Destination wallet address                           |
| `streaming_interval` | number | No       | THORChain/MAYAChain streaming interval (default `1`) |
| `streaming_quantity` | number | No       | THORChain/MAYAChain streaming sub-swap count         |
| `api_key`            | string | No       | Alternative to `Api-Key` header (see above)          |

## Response

### Headers

```
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```

### Event Sequence

Events are sent as `data: <json>\n\n` chunks (legacy SSE format — no named `event:` fields).

| Order | Event payload `type` | Description                                                          |
| ----- | -------------------- | -------------------------------------------------------------------- |
| 1     | `init`               | Stream opened, includes `quote_id` and total protocols being queried |
| 2..N  | `quote`              | One per protocol that returns a valid quote                          |
| N+1   | `final`              | Aggregated, sorted, deduplicated payload (after all settle)          |
| N+2   | `finished`           | Stream closing                                                       |

If no protocols return a usable quote, an `error` event is sent in place of `final`.

### Event Payloads

<Tabs>
  <Tab title="init">
    ```json theme={null}
    {
      "type": "init",
      "quote_id": "01953c9a-...",
      "timestamp": "2026-04-28T12:00:00.000Z",
      "total": 7
    }
    ```
  </Tab>

  <Tab title="quote">
    ```json theme={null}
    {
      "type": "quote",
      "protocol": "chainflip",
      "data": {
        "expected_amount_out": "0.0498",
        "fees": [...],
        "out_asset_decimal": 18,
        "in_asset_decimal": 8
      }
    }
    ```
  </Tab>

  <Tab title="final">
    ```json theme={null}
    {
      "type": "final",
      "quote_id": "01953c9a-...",
      "timestamp": "2026-04-28T12:00:00.000Z",
      "quotes": [
        {
          "protocol": "chainflip",
          "data": { "...": "..." },
          "expectedAmountOutNum": 0.0498,
          "totalFeesUsd": 1.23,
          "totalSwapSeconds": 120,
          "flags": ["FASTEST"]
        }
      ]
    }
    ```
  </Tab>

  <Tab title="error">
    ```json theme={null}
    {
      "type": "error",
      "code": "UNABLE_TO_RETRIEVE_QUOTES",
      "message": "...",
      "details": [
        { "protocol": "thorchain", "message": "Pool unavailable" }
      ]
    }
    ```
  </Tab>

  <Tab title="finished">
    ```json theme={null}
    { "type": "finished" }
    ```
  </Tab>
</Tabs>

### Quote Object Fields (in `final` event)

| Field                  | Description                                    |
| ---------------------- | ---------------------------------------------- |
| `expectedAmountOutNum` | Numeric expected output (for sorting)          |
| `totalFeesUsd`         | Total combined fees in USD                     |
| `totalSwapSeconds`     | Estimated time-to-settle                       |
| `flags`                | Tags like `FASTEST`, `CHEAPEST`, `BEST_OUTPUT` |

## Timeouts

* **Total budget:** 8 seconds. Protocols that haven't responded by then are dropped.
* **Per-protocol circuit breaker:** 5 seconds. Protocols that fail repeatedly are skipped.

## Examples

### Browser (EventSource)

```javascript theme={null}
const url = new URL("https://api.leokit.dev/leokit/streaming-quotes");
url.searchParams.set("from_asset", "ETH.ETH");
url.searchParams.set("to_asset", "BTC.BTC");
url.searchParams.set("amount", "1000000000000000000");
url.searchParams.set("api_key", "YOUR_API_KEY");

const es = new EventSource(url.toString());

es.onmessage = (e) => {
  const event = JSON.parse(e.data);
  switch (event.type) {
    case "init":     console.log("started", event.quote_id); break;
    case "quote":    console.log("got", event.protocol);     break;
    case "final":    console.log("done", event.quotes.length); break;
    case "finished": es.close(); break;
    case "error":    console.error(event); es.close(); break;
  }
};
```

### Server (fetch)

```javascript theme={null}
const res = await fetch(
  "https://api.leokit.dev/leokit/streaming-quotes?from_asset=ETH.ETH&to_asset=BTC.BTC&amount=1000000000000000000",
  { headers: { "Api-Key": "YOUR_API_KEY" } }
);

const reader = res.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value);
  for (const line of chunk.split("\n\n")) {
    if (!line.startsWith("data: ")) continue;
    const event = JSON.parse(line.slice(6));
    // handle event
  }
}
```

## Errors

| Status | Code                   | Description                                                |
| ------ | ---------------------- | ---------------------------------------------------------- |
| `400`  | `BAD_REQUEST`          | Missing or malformed `from_asset`, `to_asset`, or `amount` |
| `400`  | `INVALID_ASSET_FORMAT` | Asset string not in `CHAIN.SYMBOL-ADDRESS` format          |
| `401`  | `INVALID_API_KEY`      | Missing or invalid API key                                 |

The `UNABLE_TO_RETRIEVE_QUOTES` error is delivered inside the SSE stream as a `type: "error"` event, not as an HTTP error.

See the [error codes catalog](/reference/error-codes) for the full list.


## OpenAPI

````yaml /api-reference/openapi.json GET /streaming-quotes
openapi: 3.0.3
info:
  title: Leokit API
  version: 1.0.0
  description: API reference for Leokit
servers:
  - url: https://api.leokit.dev
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /streaming-quotes:
    get:
      summary: Stream quotes (SSE)
      description: >-
        Stream swap quotes as Server-Sent Events (SSE). Quotes are sent
        individually as they resolve from each protocol, followed by a final
        sorted result.
      parameters:
        - name: from_asset
          in: query
          required: true
          description: Input asset identifier.
          schema:
            type: string
          example: ETH.ETH
        - name: to_asset
          in: query
          required: true
          description: Output asset identifier.
          schema:
            type: string
          example: BTC.BTC
        - name: origin
          in: query
          required: true
          description: From wallet address.
          schema:
            type: string
          example: '0xacd9a974065de9c7a19873f95af7595e1d632167'
        - name: destination
          in: query
          required: true
          description: To wallet address.
          schema:
            type: string
          example: bc1q2hk2q3zy7pdh0tp4awzn4k5lyetedaue5zt88r
        - name: amount
          in: query
          required: true
          description: Amount to swap.
          schema:
            type: string
          example: '1'
        - name: api_key
          in: query
          required: false
          description: >-
            Alternative to Api-Key header (useful for EventSource which cannot
            set headers).
          schema:
            type: string
      responses:
        '200':
          description: >-
            Server-Sent Events stream. Events are sent as `data: {json}\n\n`
            format.
          content:
            text/event-stream:
              schema:
                type: string
                description: >-
                  SSE stream with events: init, quote (per protocol), final
                  (sorted), finished.
              example: >+
                data:
                {"type":"init","quote_id":"019b4c6e-d6c2-7000-b851-48a863f2f4b7","timestamp":"2025-12-23T18:18:14.213Z","total":5}


                data: {"type":"quote","protocol":"thorchain","data":{...}}


                data:
                {"type":"final","quotes":[...],"timestamp":"...","quote_id":"..."}


                data: {"type":"finished"}

        default:
          $ref: '#/components/responses/ErrorResponse'
components:
  responses:
    ErrorResponse:
      description: Error response
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      description: Generic error payload.
      properties:
        message:
          type: string
        code:
          type: string
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Api-Key
      description: 'Demo API-Key (Sandbox): 7037d2b3-9c76-4f62-b730-c544f7570fa4'

````