Stream quotes (SSE)
curl --request GET \
--url https://api.leokit.dev/streaming-quotes \
--header 'Api-Key: <api-key>'import requests
url = "https://api.leokit.dev/streaming-quotes"
headers = {"Api-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'Api-Key': '<api-key>'}};
fetch('https://api.leokit.dev/streaming-quotes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.leokit.dev/streaming-quotes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.leokit.dev/streaming-quotes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Api-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.leokit.dev/streaming-quotes")
.header("Api-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leokit.dev/streaming-quotes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Api-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body"data: {\"type\":\"init\",\"quote_id\":\"019b4c6e-d6c2-7000-b851-48a863f2f4b7\",\"timestamp\":\"2025-12-23T18:18:14.213Z\",\"total\":5}\n\ndata: {\"type\":\"quote\",\"protocol\":\"thorchain\",\"data\":{...}}\n\ndata: {\"type\":\"final\",\"quotes\":[...],\"timestamp\":\"...\",\"quote_id\":\"...\"}\n\ndata: {\"type\":\"finished\"}\n\n"{
"message": "<string>",
"code": "<string>"
}Endpoint Details
Streaming Quotes
Stream quote results in real-time as each protocol responds via Server-Sent Events
GET
/
streaming-quotes
Stream quotes (SSE)
curl --request GET \
--url https://api.leokit.dev/streaming-quotes \
--header 'Api-Key: <api-key>'import requests
url = "https://api.leokit.dev/streaming-quotes"
headers = {"Api-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'Api-Key': '<api-key>'}};
fetch('https://api.leokit.dev/streaming-quotes', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.leokit.dev/streaming-quotes",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.leokit.dev/streaming-quotes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Api-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.leokit.dev/streaming-quotes")
.header("Api-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leokit.dev/streaming-quotes")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Api-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body"data: {\"type\":\"init\",\"quote_id\":\"019b4c6e-d6c2-7000-b851-48a863f2f4b7\",\"timestamp\":\"2025-12-23T18:18:14.213Z\",\"total\":5}\n\ndata: {\"type\":\"quote\",\"protocol\":\"thorchain\",\"data\":{...}}\n\ndata: {\"type\":\"final\",\"quotes\":[...],\"timestamp\":\"...\",\"quote_id\":\"...\"}\n\ndata: {\"type\":\"finished\"}\n\n"{
"message": "<string>",
"code": "<string>"
}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.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 asdata: <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 |
error event is sent in place of final.
Event Payloads
- init
- quote
- final
- error
- finished
{
"type": "init",
"quote_id": "01953c9a-...",
"timestamp": "2026-04-28T12:00:00.000Z",
"total": 7
}
{
"type": "quote",
"protocol": "chainflip",
"data": {
"expected_amount_out": "0.0498",
"fees": [...],
"out_asset_decimal": 18,
"in_asset_decimal": 8
}
}
{
"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"]
}
]
}
{
"type": "error",
"code": "UNABLE_TO_RETRIEVE_QUOTES",
"message": "...",
"details": [
{ "protocol": "thorchain", "message": "Pool unavailable" }
]
}
{ "type": "finished" }
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)
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)
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 |
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 for the full list.Authorizations
Demo API-Key (Sandbox): 7037d2b3-9c76-4f62-b730-c544f7570fa4
Query Parameters
Input asset identifier.
Output asset identifier.
From wallet address.
To wallet address.
Amount to swap.
Alternative to Api-Key header (useful for EventSource which cannot set headers).
Response
Server-Sent Events stream. Events are sent as data: {json}\n\n format.
SSE stream with events: init, quote (per protocol), final (sorted), finished.