Data API
GET /v1/tokens/{mint}/holders
Holder list with supply concentration. Paginated.
Endpoint
GET https://solindex.infinityblocks.io/v1/tokens/{mint}/holders
Authentication
Send your key in the x-api-key header (or ?api-key=). See Authentication.
Path parameters
| Param | Description |
|---|---|
mint | Base-58 token mint address. |
Query parameters
| Param | Type | Default | Allowed / range |
|---|---|---|---|
limit | int | 50 | 1–500 (page size) |
offset | int | 0 | Rows to skip |
page | int | — | 1-based page (alternative to offset) |
Request
curl -s "https://solindex.infinityblocks.io/v1/tokens/<MINT>/holders?limit=50" \
-H "x-api-key: <API_KEY>"
const r = await fetch("https://solindex.infinityblocks.io/v1/tokens/<MINT>/holders?limit=50", {
headers: { "x-api-key": "<API_KEY>" },
});
const { data } = await r.json();
Response
{ "data": {
"count": 150, "top1Pct": 51.82,
"snapshotSlot": 423870739, "snapshotTime": 1780165395000, "lagSeconds": 4,
"lastChangeTime": 1780165395000,
"indexHead": { "time": 1784188941000, "slot": 433233827, "lagSeconds": 5 },
"degraded": false,
"source": "geyser-firehose-from-birth", "isComplete": true, "coverage": "complete",
"holders": [ { "wallet": "…", "balance": 30511727.23, "pct": 51.824 }, … ] },
"page": { "limit": 50, "offset": 0, "total": 150,
"returned": 50, "hasMore": true, "nextOffset": 50 } }
Paginated by
Reading freshness correctly.
limit + offset (or 1-based page); page.total equals the holder count. count and top1Pct are whole-set aggregates — they don't change as you page. Balances are aggregated by owner wallet. Burn and system sinks (the SPL incinerator and the system/null address) are excluded — they hold supply but aren't real holders, so counting them would skew count and top1Pct (this matches the common holder definition). Holder balances come from a live token-account change stream, so freshness is per-wallet, not one as-of snapshot: snapshotSlot/snapshotTime track the most-recently-updated wallet, and lagSeconds is how stale that is. source reports how the owner set was built — helius-seed+geyser-stream, geyser-firehose-from-birth, geyser-stream, helius-seed, or none. isComplete/coverage say whether the holder set is canonical (complete — fully seeded, or a LaunchLab token tracked live since launch) or a lower-bound (live-firehose-only on a pre-existing token, so small/inactive holders may be undercounted).Reading freshness correctly.
lastChangeTime (alias of snapshotTime) is when this mint’s holder set last changed on-chain — it legitimately ages on quiet tokens and is not index staleness. The index-health signal is indexHead: the live holder stream’s global watermark (time, slot, and its lag vs now). SLA: indexHead.lagSeconds is single-digit seconds in normal operation; degraded: true is returned whenever the stream watermark lags more than 15 minutes — treat responses as stale-risk only then. So: degraded=false + old lastChangeTime = “no balance changes since then”, current and authoritative. snapshotSlot=0 means the freshest row for this mint came from the external full-enumeration seed (seed rows carry no slot); the first live on-chain change writes a real slot. A reconcile sweep additionally re-verifies top holders of any mint whose trades outpace its holder rows (heals stream-disconnect windows within ~10 minutes).Try it
Run GET /v1/tokens/{mint}/holderslive · GET
Your key is stored only in this browser (localStorage).
—