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

# Assets v2 (Compact Binary)

> Compact gzipped binary asset list — same data as /leokit/assets at ~170 KB instead of ~3 MB

## Overview

`/leokit/v2/assets` returns the same supported-asset universe as [`/leokit/assets`](/api-reference/assets-balances), 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](/sdk/utilities))

Use this when payload size matters — mobile apps, embedded widgets, anywhere you'd otherwise gzip the JSON yourself.

## Endpoint

```
GET /leokit/v2/assets
```

## Request

### Authentication

Authentication is **optional**. With an `Api-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 follow `CHAIN.SYMBOL-ADDRESS` format:

```typescript theme={null}
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 (the `max-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)

```typescript theme={null}
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 `LK02` magic + 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`](/api-reference/assets-balances).


## OpenAPI

````yaml /api-reference/openapi.json GET /v2/assets
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:
  /v2/assets:
    get:
      summary: Compact binary asset list (gzipped)
      description: >-
        Same data as /assets but in a compact binary format. ~170 KB gzipped vs
        ~3 MB JSON. See the documentation page for the wire format and a
        TypeScript decoder.
      responses:
        '200':
          description: Gzipped binary blob.
          headers:
            Content-Encoding:
              schema:
                type: string
                enum:
                  - gzip
            Cache-Control:
              schema:
                type: string
                example: public, max-age=20
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '503':
          description: Asset list not yet loaded (server just restarted).
          headers:
            Retry-After:
              schema:
                type: integer
          content:
            text/plain:
              schema:
                type: string
                example: Assets not yet loaded
      security:
        - {}
        - ApiKeyAuth: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Api-Key
      description: 'Demo API-Key (Sandbox): 7037d2b3-9c76-4f62-b730-c544f7570fa4'

````