Skip to main content
API referenceSubnets

Subnet Holders

Last updated

GET
/api/v1/subnets/{netuid}/holders

Fetch who owns one subnet's alpha (#9557) — the top coldkeys by alpha held on that netuid, with each holder's share of the subnet total, how many hotkeys they hold it through, and whole-subnet aggregates (distinct holder count, total measured alpha, top5/top10/top20 concentration). The reverse index of /accounts/{ss58}/positions, which reads the same ledger one coldkey (one account) at a time. Distinct from /subnets/{netuid}/concentration, which computes its scalars off neurons.stake_tao and therefore sees REGISTERED UIDs only: this reads nominator_positions, keyed on (coldkey, hotkey, netuid) whether or not the hotkey holds a UID on the subnet, so alpha staked to UNREGISTERED hotkeys is included — on netuid 74, 92 hotkeys carry positions and 10 are registered there, which is the part no other public source reaches without a full-chain map scan. Valued as share_fraction x hotkey_alpha.total_alpha against ONE proven pool pass, and ranked in ALPHA rather than TAO: within a single subnet alpha is already a common unit, so there is no subnet_snapshots price join and none of its up-to-24h staleness — multiply by the subnet's alpha_price_tao for TAO. limit caps the returned rows (default 20, max 100); holder_count, total_alpha and the three concentration shares are computed across the FULL holder set and then sliced, never over the capped rows, because the top of a sum is not contained in the union of the tops of its addends. TWO STATES DECLINE rather than answer, both with holders:[] plus a degraded.reason and NULL counts: pool_totals_unproven while no hotkey_alpha pass is recorded complete (a partially loaded pool ledger silently UNDERPRICES holders rather than visibly dropping them, so the ranking would be plausible and wrong), and root_not_in_alpha_map for netuid 0, which SubtensorModule::Alpha does not cover at all. A zero in any count is therefore a measured zero, never a decline. Mainnet-only: neither source table carries a network dimension.

Path Parameters

netuid*integer
Range0 <= value <= 65535

Query Parameters

limit?integer

Maximum number of rows to return in one page (at most 100). Routes differ in how they handle a larger value: some reject it with 400 invalid_query, others clamp to the maximum and answer 200. Read the limit echoed in the response body rather than assuming the page is the size you asked for.

Range1 <= value <= 100

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/api/v1/subnets/0/holders"
{  "data": {    "captured_at": "2026-06-01T00:00:00.000Z",    "concentration": {      "top10_share": 0.5,      "top20_share": 0.5,      "top5_share": 0.5    },    "degraded": {      "reason": "pool_totals_unproven"    },    "holder_count": 1,    "holders": [      {        "alpha": 0.5,        "coldkey": "example",        "hotkey_count": 1,        "share_of_total": 0.5      }    ],    "limit": 1,    "netuid": 7,    "positions_captured_at": "2026-06-01T00:00:00.000Z",    "schema_version": 1,    "total_alpha": 0.5  },  "meta": {    "artifact_path": "example",    "cache": "short",    "contract_version": "2026-06-29.1",    "generated_at": "2026-06-01T00:00:00.000Z",    "pagination": {      "collection": "example",      "cursor": 1,      "limit": 1,      "next_cursor": 1,      "order": "asc",      "returned": 1,      "sort": "example",      "total": 1    },    "published_at": "2026-06-01T00:00:00.000Z",    "source": "live-cron-prober",    "stale_contract": {      "built_under": "example",      "live": "example"    }  },  "ok": true,  "schema_version": 1}