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

# Tokens and Market Data

> Read Blank tokens, price snapshots, candles, trades, and holder stats. No API key required.

# Tokens and Market Data

Every operation on this page is anonymous. No API key, no scope, no wallet. They are public reads, they are cached at the edge, and they all support conditional requests.

| Operation                | HTTP                         | SDK                                              |
| ------------------------ | ---------------------------- | ------------------------------------------------ |
| `listTokens`             | `GET /tokens`                | `blank.tokens.list()` / `blank.tokens.iterate()` |
| `getToken`               | `GET /tokens/{mint}`         | `blank.tokens.get(mint)`                         |
| `getTokenMarketSnapshot` | `GET /tokens/{mint}/market`  | `blank.marketData.snapshot(mint)`                |
| `listTokenCandles`       | `GET /tokens/{mint}/candles` | `blank.marketData.candles(mint, query)`          |
| `listTokenTrades`        | `GET /tokens/{mint}/trades`  | `blank.marketData.trades(mint, query)`           |
| `getTokenHolderStats`    | `GET /tokens/{mint}/holders` | `blank.marketData.holders(mint)`                 |

## Setup

```ts theme={null}
import { BlankClient } from "@blankdotbuild/sdk";

const blank = new BlankClient();
```

The base URL defaults to `https://api.blank.build/api/v2`. Every call returns `{ data, metadata, response }`, and `metadata.requestId` is the `X-Request-Id` you should log alongside failures.

## Numbers are strings

Every monetary value, price, and amount is a **canonical decimal string**. Raw on-chain amounts are unsigned integer strings. Nothing is a JSON number, so nothing loses precision in transit.

<Warning>
  Do not parse these with `Number` or `parseFloat`. Token supplies and lamport
  amounts exceed the safe integer range, and binary floats cannot represent
  decimal prices exactly. Use a decimal library, or `BigInt` for raw integer
  strings.
</Warning>

Integer counts (`holderCount`, `activeStakers`, `trades`, `rank`) are real JSON numbers. Timestamps are RFC 3339 strings.

## List tokens

Returns tokens in reverse launch order.

```ts theme={null}
const { data } = await blank.tokens.list({ status: "bonding", limit: 25 });

for (const token of data.data) {
  console.log(token.symbol, token.currentPriceInSol);
}
```

| Query        | Type                 | Notes                                         |
| ------------ | -------------------- | --------------------------------------------- |
| `cursor`     | string               | Opaque. Pass back `page.nextCursor` verbatim. |
| `limit`      | integer              | 1–100. Defaults to 50.                        |
| `status`     | `TokenStatus`        | Filter by lifecycle status.                   |
| `launchType` | `dbc` \| `pre_raise` | Filter by launch mechanism.                   |

The response is `{ data: [...], page: { nextCursor, hasMore } }`. See [Conventions](/docs/for-developers/conventions) for the full pagination contract.

### Token fields

| Field               | Type                 | Notes                            |
| ------------------- | -------------------- | -------------------------------- |
| `mintAddress`       | string               | The token mint.                  |
| `name`              | string               |                                  |
| `symbol`            | string               |                                  |
| `description`       | string \| null       |                                  |
| `imageUrl`          | string \| null       |                                  |
| `status`            | `TokenStatus`        | See below.                       |
| `launchType`        | `dbc` \| `pre_raise` |                                  |
| `creatorWallet`     | string               |                                  |
| `currentPriceInSol` | decimal string       |                                  |
| `solCollectedInSol` | decimal string       |                                  |
| `totalSupply`       | decimal string       |                                  |
| `launchedAt`        | RFC 3339             |                                  |
| `graduatedAt`       | RFC 3339 \| null     | Null until graduation completes. |

### TokenStatus

`pending`, `pending_launch_init`, `pending_escrows`, `pending_finalization`, `bonding`, `pre_raise_pending`, `pre_raising`, `pre_raise_settling`, `pre_raise_refunding`, `pre_raise_failed`, `graduating_migration_pending`, `graduated`, `graduation_failed`, `expired`, `cancelled`, `failed`.

Treat this as an open enum in your own code. Match the statuses you care about and fall through on the rest.

## Get one token

```ts theme={null}
const { data: token } = await blank.tokens.get(mint);
```

Same fields as the list entries. Unknown mints return `404 token_not_found`.

## Paginate the full set

`blank.tokens.iterate()` handles cursors for you and yields tokens one at a time. `maxPages` is a required safety bound in practice — it defaults to 100 and throws if the walk runs past it.

```ts theme={null}
const graduated: string[] = [];

for await (const token of blank.tokens.iterate(
  { status: "graduated", limit: 100 },
  { maxPages: 5 }
)) {
  graduated.push(token.mintAddress);
}
```

## Market snapshot

The current price, cap, and liquidity for one token.

```ts theme={null}
const { data: market } = await blank.marketData.snapshot(mint);
```

| Field            | Type              |
| ---------------- | ----------------- |
| `mintAddress`    | string            |
| `priceInSol`     | decimal string    |
| `marketCapInSol` | decimal string    |
| `liquidityInSol` | decimal string    |
| `totalSupply`    | decimal string    |
| `holderCount`    | integer \| null   |
| `asOf`           | RFC 3339          |
| `provenance`     | `"blank-indexer"` |

`asOf` is when the indexer computed the snapshot, not when you called. Show it if your UI implies live pricing.

## Candles

```ts theme={null}
const { data: series } = await blank.marketData.candles(mint, {
  from: "2026-08-01T00:00:00Z",
  to: "2026-08-02T00:00:00Z",
  interval: "1h",
});
```

`from`, `to`, and `interval` are all required. Timestamps are RFC 3339 with an offset, and `from` must be strictly before `to`. The range is half-open: `from` is inclusive, `to` is exclusive. Intervals: `1m`, `5m`, `15m`, `1h`, `4h`, `1d`.

The `CandleSeries` response carries `data`, `interval`, `from`, `to`, and `provenance` (`"blank-indexer"`). Each candle has `time`, `open`, `high`, `low`, `close`, `volumeInSol` (decimal strings) and `trades` (integer).

<Note>
  A series is capped at **1000 candles**. A window that would exceed the cap, or
  that is otherwise out of range, returns `422 market_time_range_invalid`. Split
  long ranges into consecutive windows, or step up to a coarser interval.
</Note>

## Trades

Newest first, cursor paginated with `cursor` and `limit`.

```ts theme={null}
const { data } = await blank.marketData.trades(mint, { limit: 50 });
```

| Field                  | Type                   |
| ---------------------- | ---------------------- |
| `id`                   | uuid                   |
| `type`                 | `buy` \| `sell`        |
| `traderWallet`         | string                 |
| `tokenAmount`          | decimal string         |
| `solAmount`            | decimal string         |
| `pricePerTokenInSol`   | decimal string         |
| `transactionSignature` | string                 |
| `slot`                 | numeric string \| null |
| `blockTime`            | RFC 3339               |

## Holder stats

```ts theme={null}
const { data: holders } = await blank.marketData.holders(mint);
```

Returns `mintAddress`, `holderCount` (integer), `slot` (numeric string or null), `asOf`, and `provenance`.

`provenance` tells you how the count was derived: `live_trade_index` (running index, freshest), `trade_replay` (rebuilt from trade history), or `onchain_snapshot` (read from chain state). A token with no computed stats returns `404 holder_stats_not_found`.

## Caching and conditional requests

Every response carries an `ETag`. Send it back as `If-None-Match` and you get `304 Not Modified` with no body when nothing changed.

| Operation                | `Cache-Control`                                  |
| ------------------------ | ------------------------------------------------ |
| `listTokens`, `getToken` | `public, max-age=10, stale-while-revalidate=30`  |
| `getTokenMarketSnapshot` | `public, max-age=2, stale-while-revalidate=10`   |
| `listTokenCandles`       | `public, max-age=30, stale-while-revalidate=120` |
| `listTokenTrades`        | `public, max-age=2, stale-while-revalidate=10`   |
| `getTokenHolderStats`    | `public, max-age=10, stale-while-revalidate=30`  |

Polling faster than `max-age` gains you nothing but rate-limit budget. Candles use the `public-read-expensive` rate policy; the other five use `public-read-cheap`. Both are documented in [Conventions](/docs/for-developers/conventions).

```bash theme={null}
curl -sS -D - -o /dev/null \
  -H 'If-None-Match: "8SdM3s0Xh2Q0oRr1kq8m3wYt7vB2cN5pL9aZ1fJ4uKk"' \
  https://api.blank.build/api/v2/tokens/So11111111111111111111111111111111111111112/market
```

No `Authorization` header — these endpoints are anonymous, so this request is safe to make from anywhere.

## Errors

| Code                        | Status | Meaning                                                       |
| --------------------------- | ------ | ------------------------------------------------------------- |
| `cursor_invalid`            | 400    | The cursor is malformed or no longer valid. Restart the walk. |
| `token_not_found`           | 404    | No public token exists for that mint.                         |
| `holder_stats_not_found`    | 404    | The token exists, but no holder snapshot has been indexed.    |
| `market_time_range_invalid` | 422    | The candle window is out of range.                            |
| `token_index_unavailable`   | 503    | The token index is temporarily unavailable. Retry.            |
| `market_data_unavailable`   | 503    | Market data is temporarily unavailable. Retry.                |

Full problem-details shape and retry guidance live in [Errors](/docs/reference/errors).

## Next

* [Fees](/docs/for-developers/fee-status) — fee state and distribution history.
* [Staking](/docs/for-developers/developer-staking) — pools, leaderboards, positions.
* [Conventions](/docs/for-developers/conventions) — pagination, rate limits, idempotency.
