Compact binary asset list (gzipped)
curl --request GET \
--url https://api.leokit.dev/v2/assets \
--header 'Api-Key: <api-key>'import requests
url = "https://api.leokit.dev/v2/assets"
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/v2/assets', 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/v2/assets",
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/v2/assets"
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/v2/assets")
.header("Api-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leokit.dev/v2/assets")
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"<string>""Assets not yet loaded"Endpoint Details
Assets v2 (Compact Binary)
Compact gzipped binary asset list — same data as /leokit/assets at ~170 KB instead of ~3 MB
GET
/
v2
/
assets
Compact binary asset list (gzipped)
curl --request GET \
--url https://api.leokit.dev/v2/assets \
--header 'Api-Key: <api-key>'import requests
url = "https://api.leokit.dev/v2/assets"
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/v2/assets', 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/v2/assets",
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/v2/assets"
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/v2/assets")
.header("Api-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.leokit.dev/v2/assets")
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"<string>""Assets not yet loaded"Overview
/leokit/v2/assets returns the same supported-asset universe as /leokit/assets, but in a compact binary format:
- ~170 KB gzipped vs ~3 MB JSON
- 5,000+ tokens with prices in a single response
- Per-client cache, pre-built at asset-refresh time (no on-request serialization)
- Strips icon URLs (clients derive them from the CDN pattern)
Endpoint
GET /leokit/v2/assets
Request
Authentication
Authentication is optional. With anApi-Key, disabled_chains and client-restricted tokens are filtered out. Without one, the public asset universe is returned.
Response
Headers
Content-Type: application/octet-stream
Content-Encoding: gzip
Cache-Control: public, max-age=20
Wire Format
The body is a single gzipped blob. After decompression:[ Header (9 bytes) ]
magic : 4 bytes ASCII = "LK02"
version : 1 byte = 2
timestamp : 4 bytes BE uint32 (unix seconds)
[ Chain Dictionary ]
chain_count : 2 bytes BE uint16
for each chain:
name_len : 1 byte
name : N bytes UTF-8 (e.g. "ETH", "BTC", "BSC")
[ Address Dictionary ]
address_count: 2 bytes BE uint16
for each address:
addr_len : 1 byte
address : N bytes UTF-8
[ Token Records ]
token_count : 3 bytes BE uint24
for each token:
chain_idx : 1 byte (index into Chain Dictionary)
sym_len : 1 byte
symbol : N bytes UTF-8 (e.g. "USDC", "ETH")
addr_idx : 2 bytes BE uint16 (index into Address Dictionary, 0xFFFF = native)
decimals : 1 byte
price_usd : 4 bytes BE float32
flags : 1 byte (bit 0 = is_popular)
Reconstructing an Asset ID
Asset IDs followCHAIN.SYMBOL-ADDRESS format:
const chain = chains[record.chain_idx];
const symbol = record.symbol;
const address = record.addr_idx === 0xFFFF
? null
: addresses[record.addr_idx];
const assetId = address
? `${chain}.${symbol}-${address}`
: `${chain}.${symbol}`;
Caching
The full binary is per-client-pre-built server-side and refreshed when the global asset list updates. Clients should cache locally for 20 seconds (themax-age value).
Errors
| Status | Description |
|---|---|
503 | Asset list not yet loaded (server just restarted). Body is plain text "Assets not yet loaded". Retry after the Retry-After header (seconds). |
Decoder Example (TypeScript)
async function fetchV2Assets(apiKey?: string): Promise<Asset[]> {
const res = await fetch("https://api.leokit.dev/leokit/v2/assets", {
headers: apiKey ? { "Api-Key": apiKey } : {},
});
// Browser/Bun: response is auto-decompressed
const buf = new Uint8Array(await res.arrayBuffer());
const view = new DataView(buf.buffer);
const decoder = new TextDecoder();
// Header
const magic = decoder.decode(buf.subarray(0, 4)); // "LK02"
if (magic !== "LK02") throw new Error("Bad magic");
const version = view.getUint8(4);
const timestamp = view.getUint32(5, false);
let off = 9;
// Chain dictionary
const chainCount = view.getUint16(off, false); off += 2;
const chains: string[] = [];
for (let i = 0; i < chainCount; i++) {
const len = view.getUint8(off); off += 1;
chains.push(decoder.decode(buf.subarray(off, off + len)));
off += len;
}
// Address dictionary
const addrCount = view.getUint16(off, false); off += 2;
const addresses: string[] = [];
for (let i = 0; i < addrCount; i++) {
const len = view.getUint8(off); off += 1;
addresses.push(decoder.decode(buf.subarray(off, off + len)));
off += len;
}
// Tokens
const tokenCount =
(view.getUint8(off) << 16) |
(view.getUint8(off + 1) << 8) |
view.getUint8(off + 2);
off += 3;
const tokens: Asset[] = [];
for (let i = 0; i < tokenCount; i++) {
const chainIdx = view.getUint8(off); off += 1;
const symLen = view.getUint8(off); off += 1;
const symbol = decoder.decode(buf.subarray(off, off + symLen));
off += symLen;
const addrIdx = view.getUint16(off, false); off += 2;
const decimals = view.getUint8(off); off += 1;
const priceUsd = view.getFloat32(off, false); off += 4;
const flags = view.getUint8(off); off += 1;
const chain = chains[chainIdx];
const address = addrIdx === 0xFFFF ? null : addresses[addrIdx];
tokens.push({
assetId: address ? `${chain}.${symbol}-${address}` : `${chain}.${symbol}`,
chain,
symbol,
address,
decimals,
priceUsd,
isPopular: (flags & 1) === 1,
});
}
return tokens;
}
Notes
- The binary format is versioned via the
LK02magic + version byte. Future formats (LK03, etc.) will increment the magic. Always validate the magic before parsing. - Only transparent UTXO addresses are included. Shielded ZEC tokens are not part of the asset list.
- For richer per-token metadata (icons, descriptions, etc.) use
/leokit/assets.
Authorizations
Demo API-Key (Sandbox): 7037d2b3-9c76-4f62-b730-c544f7570fa4
Response
Gzipped binary blob.
The response is of type file.