# Taostats Docs — New API reference > Taostats is the block explorer, data API and analytics platform for Bittensor and the TAO token. These docs cover the Taostats API, the MCP server, the TypeScript SDK, Bittensor Auth, the apps, and the Bittensor concepts behind them. The "New API reference" section of https://taostats.io/docs. The index of every docs page is https://taostats.io/llms.txt and the whole corpus is https://taostats.io/llms-full.txt. Each page below is also available on its own at the `_Source:` URL with `.md` appended. --- # New API reference Every Taostats API endpoint — 124 of them across 20 groups — generated from the live OpenAPI spec. _Source: https://taostats.io/docs/new_ _Last reviewed: 2026-10-07_ **124 endpoints** across **20 groups**, generated from the live OpenAPI spec. Pick a category below, or use the sidebar to jump straight to a group. The base URL is `https://api.taostats.io`. Every request needs an `Authorization` header holding your API key — no `Bearer` prefix. ## Accounts & balances - [Accounts](https://taostats.io/docs/new/accounts) — 6 endpoints - [Alpha positions](https://taostats.io/docs/new/alpha) — 5 endpoints - [Transfers](https://taostats.io/docs/new/transfers) — 1 endpoint ## Subnets - [Subnets](https://taostats.io/docs/new/subnets) — 34 endpoints ## Validators & miners - [Validators](https://taostats.io/docs/new/validators) — 14 endpoints - [Miners](https://taostats.io/docs/new/miners) — 4 endpoints ## Blocks, chain & network - [Chain (blocks, calls, extrinsics, events)](https://taostats.io/docs/new/chain) — 5 endpoints - [Network](https://taostats.io/docs/new/network) — 5 endpoints - [Pre-dTAO history](https://taostats.io/docs/new/historic) — 1 endpoint - [EVM](https://taostats.io/docs/new/evm) — 6 endpoints - [Contract events](https://taostats.io/docs/new/contract-events) — 1 endpoint ## Prices & markets - [Price](https://taostats.io/docs/new/price) — 4 endpoints - [Tokenomics](https://taostats.io/docs/new/tokenomics) — 1 endpoint - [TradingView](https://taostats.io/docs/new/tradingview) — 3 endpoints - [CoinGecko](https://taostats.io/docs/new/coingecko) — 4 endpoints - [CoinMarketCap](https://taostats.io/docs/new/cmc) — 7 endpoints - [OTC](https://taostats.io/docs/new/otc) — 19 endpoints ## Accounting & tax - [Tax](https://taostats.io/docs/new/tax) — 2 endpoints ## Live RPC - [RPC](https://taostats.io/docs/new/rpc) — 1 endpoint ## Meta - [Status](https://taostats.io/docs/new/status) — 1 endpoint ## Looking for something specific - [OpenAPI JSON](https://api.taostats.io/openapi.json) — The machine-readable spec this reference is generated from. - [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api) — Authentication, rate limits and your first request. - [Get an API key](https://taostats.io/pro/api-keys) — Create and manage keys in Taostats Pro. There is a free tier. - [Sample apps](https://github.com/taostat/awesome-taostats-api-examples) — TypeScript examples for every endpoint. --- # Accounts Every Taostats API endpoint in the Accounts group, with its method and path. _Source: https://taostats.io/docs/new/accounts_ _Last reviewed: 2026-10-07_ 6 Accounts endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Accounts Address History](https://taostats.io/docs/new/accounts/get-accounts-address-history) | `GET` | `/v1/accounts/{address}/history` | | [Get Accounts Address](https://taostats.io/docs/new/accounts/get-accounts-address) | `GET` | `/v1/accounts/{address}` | | [Get Accounts Address Identity](https://taostats.io/docs/new/accounts/get-accounts-address-identity) | `GET` | `/v1/accounts/{address}/identity` | | [Get Accounts Identities](https://taostats.io/docs/new/accounts/get-accounts-identities) | `GET` | `/v1/accounts/identities` | | [Get Accounts Leaderboard](https://taostats.io/docs/new/accounts/get-accounts-leaderboard) | `GET` | `/v1/accounts/leaderboard` | | [Get Accounts Pending Coldkey Swaps](https://taostats.io/docs/new/accounts/get-accounts-pending-coldkey-swaps) | `GET` | `/v1/accounts/pending-coldkey-swaps` | --- # Get Accounts Address History Account balance over time. _Source: https://taostats.io/docs/new/accounts/get-accounts-address-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/{address}/history ``` Requires an API key in the `Authorization` header. Account balance over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/{address}/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/{address}/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/{address}/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `address` | path | `string` | Yes | SS58 or 0x-hex account address | | `network` | query | `string` | | Network filter. One of `all`, `finney`, `nakamoto`, `kusanagi`. Default `all`: every network's rows, newest first, as the OLD API returns when `network` is omitted. `nakamoto` and `kusanagi` return that chain's daily balances, each row's `block_number` and `timestamp` being that chain's own; `block_start`/`block_end` apply to each row's own chain. Invalid values are rejected with a 400. One of `all`, `finney`, `nakamoto`, `kusanagi`. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated account balance history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].address` | `string` | Yes | SS58 address. | | `data[].balance_free` | `string` | Yes | Free balance (RAO, u64 as string). | | `data[].balance_reserved` | `string` | Yes | Reserved balance (RAO, u64 as string). | | `data[].balance_staked` | `string` | Yes | Total staked (RAO, u64 as string). | | `data[].balance_staked_alpha_as_tao` | `string` | Yes | Alpha-staked as TAO equivalent (RAO, u64 as string). | | `data[].balance_staked_root` | `string` | Yes | Root-staked (RAO, u64 as string). | | `data[].balance_total` | `string` | Yes | Total balance (RAO, u64 as string). | | `data[].block_number` | `integer (int32)` | Yes | Block number of the snapshot. | | `data[].created_on_date` | `string, nullable` | Yes | Account creation date (YYYY-MM-DD) from `account_created_at_v1`. `null` only when the account has no created-at row. | | `data[].created_on_network` | `string, nullable` | Yes | Network the account was created on (from `account_created_at_v1`). `null` only when the account has no created-at row. | | `data[].network` | `string` | Yes | Network name: `"finney"`, except on history rows, which carry their own network (`kusanagi` or `nakamoto` rows come back for those networks and for `network=all` or no `network`). See module docs. | | `data[].rank` | `integer (int32)` | Yes | Leaderboard rank at this snapshot. | | `data[].root_basket_claimable_tao` | `string, nullable` | Yes | TAO claimable from this coldkey's root beta baskets at this snapshot (RAO, u64 as string) — what redeeming every basket share would have realized at that block. `null` means **unknown**, never zero. It is populated on `/v1/accounts/{address}/history` from the daily snapshot block, and is `null` on rows before basket state existed on chain (below block 8,765,684, i.e. before 2026-08-03). On `/v1/accounts/leaderboard` it is always `null`: the figure needs a per-coldkey computation the *latest* snapshot does not carry, and the old API is the same — it fills the field for one address, never for a list. **Not part of `balance_total`.** It is an entitlement held by the funds, not a balance the coldkey holds, so it cannot move the rank. | | `data[].root_claim_type` | `string, nullable` | Yes | Always `null`. The root-claim preference was deleted from the chain at spec 441 and is no longer served (#774, see module docs); the field is kept because the OLD API returns it as `null`. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp, e.g. `"2026-06-13T09:10:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Accounts Address Single account detail with 24hr-ago comparison. _Source: https://taostats.io/docs/new/accounts/get-accounts-address_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/{address} ``` Requires an API key in the `Authorization` header. Single account detail with 24hr-ago comparison. Returns a single-element `data` array (no pagination). A valid address with no stored row gets a zero row, except a protocol account, which gets a 404 (see [`unseen_account_detail`]). ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/{address}" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/{address}', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/{address}", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `address` | path | `string` | Yes | SS58 or 0x-hex account address | ## Responses ### `200` — Account detail | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].address` | `string` | Yes | SS58 address. | | `data[].alpha_balances` | `array, nullable` | Yes | Per-subnet alpha positions, read live from chain. `null` when the live read is unavailable (chain unconfigured or read failed), or for an address with no stored row, whose positions are not known (#1304). | | `data[].alpha_balances[].balance` | `string` | Yes | Alpha balance (RAO, u64 as string). | | `data[].alpha_balances[].balance_as_tao` | `string` | Yes | Alpha as TAO equivalent (RAO, u64 as string). | | `data[].alpha_balances[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].alpha_balances[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].alpha_balances[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].alpha_balances_24hr_ago` | `array, nullable` | Yes | Per-subnet alpha positions 24hr ago (chain @ `head − 7200`, priced from `subnet_pool_v1`). `null` if the 24h-ago read is unavailable, or for an address with no stored row (#1304). | | `data[].alpha_balances_24hr_ago[].balance` | `string` | Yes | Alpha balance (RAO, u64 as string). | | `data[].alpha_balances_24hr_ago[].balance_as_tao` | `string` | Yes | Alpha as TAO equivalent (RAO, u64 as string). | | `data[].alpha_balances_24hr_ago[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].alpha_balances_24hr_ago[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].alpha_balances_24hr_ago[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].balance_free` | `string` | Yes | Free balance (RAO, u64 as string). | | `data[].balance_free_24hr_ago` | `string, nullable` | Yes | Free balance 24hr ago (RAO, u64 as string), read from chain at block `head − 7200`. `null` only if the 24h-ago read is unavailable (see [`account_detail`]). For an address with no stored row, this and every other `*_24hr_ago` balance is `"0"` from the snapshot, not the chain (see [`unseen_account_detail`]). | | `data[].balance_reserved` | `string` | Yes | Reserved balance (RAO, u64 as string). | | `data[].balance_reserved_24hr_ago` | `string, nullable` | Yes | Reserved balance 24hr ago (RAO, u64 as string). | | `data[].balance_staked` | `string` | Yes | Total staked (RAO, u64 as string). | | `data[].balance_staked_24hr_ago` | `string, nullable` | Yes | Total staked 24hr ago (RAO, u64 as string). | | `data[].balance_staked_alpha_as_tao` | `string` | Yes | Alpha-staked as TAO equivalent (RAO, u64 as string). | | `data[].balance_staked_alpha_as_tao_24hr_ago` | `string, nullable` | Yes | Alpha-staked as TAO 24hr ago (RAO, u64 as string). | | `data[].balance_staked_root` | `string` | Yes | Root-staked (RAO, u64 as string). | | `data[].balance_staked_root_24hr_ago` | `string, nullable` | Yes | Root-staked 24hr ago (RAO, u64 as string). | | `data[].balance_total` | `string` | Yes | Total balance (RAO, u64 as string). | | `data[].balance_total_24hr_ago` | `string, nullable` | Yes | Total balance 24hr ago (RAO, u64 as string). | | `data[].block_number` | `integer (int32)` | Yes | Block number. | | `data[].coldkey_swap` | `object, nullable` | Yes | The executed coldkey swap this account was involved in, as the old or the new coldkey, from `coldkey_swap_v1`. `null` if it was never swapped. | | `data[].coldkey_swap.block_number` | `integer (int32)` | Yes | Swap block. | | `data[].coldkey_swap.network` | `string` | Yes | Network. | | `data[].coldkey_swap.new_coldkey` | `string` | Yes | New coldkey (SS58). | | `data[].coldkey_swap.old_coldkey` | `string` | Yes | Old coldkey (SS58). | | `data[].coldkey_swap.timestamp` | `string` | Yes | Swap time (ISO 8601). | | `data[].created_on_date` | `string, nullable` | Yes | Account creation date (YYYY-MM-DD) from `account_created_at_v1`. `null` only when the account has no created-at row. | | `data[].created_on_network` | `string, nullable` | Yes | Network the account was created on (from `account_created_at_v1`). `null` only when the account has no created-at row. | | `data[].network` | `string` | Yes | Network name. Always `"finney"`. | | `data[].rank` | `integer (int32)` | Yes | Leaderboard rank. | | `data[].root_basket_claimable_tao` | `string, nullable` | Yes | TAO this coldkey could realize **right now** by redeeming its root beta-basket shares across every validator it stakes to (RAO, u64 as string) — read live from the chain's own `BetaBasketRuntimeApi::get_root_basket_owed`, at the same finalized head as the balances above. `null` means **unknown**, never zero: no chain client, the live read failed, the runtime predates Root Reborn (the API does not exist below release 450), or the address has no stored row (#1304). A coldkey that genuinely has nothing owed returns `"0"`. The daily historical series is on `/v1/accounts/{address}/history`, computed by the indexer rather than read live. **Not part of `balance_total`** — it is an entitlement held by the funds, not a balance the coldkey holds. | | `data[].root_claim_type` | `string, nullable` | Yes | Always `null`: the root-claim preference was deleted from the chain at spec 441 and is no longer served (#774, see module docs). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp. | ### `400` — Invalid address | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — A protocol account (subnet reserve and other pallet accounts), which the snapshot leaves out | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Accounts Address Identity One wallet's on-chain identity, as a single-element data array. _Source: https://taostats.io/docs/new/accounts/get-accounts-address-identity_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/{address}/identity ``` Requires an API key in the `Authorization` header. One wallet's on-chain identity, as a single-element `data` array. 404 when the coldkey has no identity on chain now, including one that was removed. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/{address}/identity" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/{address}/identity', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/{address}/identity", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `address` | path | `string` | Yes | SS58 or 0x-hex coldkey | ## Responses ### `200` — The wallet's on-chain identity | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].additional` | `string, nullable` | Yes | The identity's free-form `additional` field. | | `data[].address` | `string` | Yes | The coldkey the identity belongs to (SS58). | | `data[].description` | `string, nullable` | Yes | Description. | | `data[].discord` | `string, nullable` | Yes | Discord, as set on chain. | | `data[].github_repo` | `string, nullable` | Yes | GitHub repository, as set on chain. | | `data[].image` | `string, nullable` | Yes | Image URL, as set on chain. | | `data[].name` | `string, nullable` | Yes | Display name. `null` when the identity sets none. | | `data[].url` | `string, nullable` | Yes | Website, as set on chain. | | `data[].validator_hotkeys` | `array` | Yes | Hotkeys this coldkey owns that are validators today (the owner `/v1/validators` shows), sorted. Empty when it owns none. | | `data[].validator_hotkeys[]` | `string` | Yes | | ### `400` — Invalid address | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — The wallet has no on-chain identity | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Accounts Identities Every wallet with an on-chain identity, ordered by address. _Source: https://taostats.io/docs/new/accounts/get-accounts-identities_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/identities ``` Requires an API key in the `Authorization` header. Every wallet with an on-chain identity, ordered by address. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/identities" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/identities', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/identities", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `address` | query | `string` | | Only this coldkey (SS58 or 0x-hex). A coldkey with no identity gives an empty page, not a 404. | | `validator_hotkey` | query | `string` | | Only the coldkey that owns this validator hotkey today (SS58 or 0x-hex). A hotkey that is not a validator today gives an empty page. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000; a page past it is rejected with a 400. The listing holds about 1,000 rows. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | ## Responses ### `200` — On-chain identities, one per coldkey | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].additional` | `string, nullable` | Yes | The identity's free-form `additional` field. | | `data[].address` | `string` | Yes | The coldkey the identity belongs to (SS58). | | `data[].description` | `string, nullable` | Yes | Description. | | `data[].discord` | `string, nullable` | Yes | Discord, as set on chain. | | `data[].github_repo` | `string, nullable` | Yes | GitHub repository, as set on chain. | | `data[].image` | `string, nullable` | Yes | Image URL, as set on chain. | | `data[].name` | `string, nullable` | Yes | Display name. `null` when the identity sets none. | | `data[].url` | `string, nullable` | Yes | Website, as set on chain. | | `data[].validator_hotkeys` | `array` | Yes | Hotkeys this coldkey owns that are validators today (the owner `/v1/validators` shows), sorted. Empty when it owns none. | | `data[].validator_hotkeys[]` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Accounts Leaderboard Browse and rank accounts by balance. _Source: https://taostats.io/docs/new/accounts/get-accounts-leaderboard_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/leaderboard ``` Requires an API key in the `Authorization` header. Browse and rank accounts by balance. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/leaderboard" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/leaderboard', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/leaderboard", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `balance_free_min` | query | `integer (int64)` | | Minimum free balance (RAO). | | `balance_free_max` | query | `integer (int64)` | | Maximum free balance (RAO). | | `balance_staked_min` | query | `integer (int64)` | | Minimum staked balance (RAO). | | `balance_staked_max` | query | `integer (int64)` | | Maximum staked balance (RAO). | | `balance_staked_root_min` | query | `integer (int64)` | | Minimum root-staked balance (RAO). | | `balance_staked_root_max` | query | `integer (int64)` | | Maximum root-staked balance (RAO). | | `balance_staked_alpha_as_tao_min` | query | `integer (int64)` | | Minimum alpha-staked-as-TAO balance (RAO). | | `balance_staked_alpha_as_tao_max` | query | `integer (int64)` | | Maximum alpha-staked-as-TAO balance (RAO). | | `balance_total_min` | query | `integer (int64)` | | Minimum total balance (RAO). | | `balance_total_max` | query | `integer (int64)` | | Maximum total balance (RAO). | | `rank` | query | `integer (int32)` | | Exact leaderboard rank. | | `created_on_network` | query | `string` | | Network the account was created on (`finney` / `kusanagi` / `nakamoto`). Filters by the eldest observation in `account_created_at_v1`. | | `created_on_timestamp_start` | query | `integer (int64)` | | Account-creation-time range start (unix seconds, inclusive) — filters on the eldest observation's timestamp. | | `created_on_timestamp_end` | query | `integer (int64)` | | Account-creation-time range end (unix seconds, inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `balance_total`. One of `balance_free`, `balance_staked`, `balance_staked_root`, `balance_staked_alpha_as_tao`, `balance_total`, `created_at_timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated leaderboard of accounts | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].address` | `string` | Yes | SS58 address. | | `data[].balance_free` | `string` | Yes | Free balance (RAO, u64 as string). | | `data[].balance_reserved` | `string` | Yes | Reserved balance (RAO, u64 as string). | | `data[].balance_staked` | `string` | Yes | Total staked (RAO, u64 as string). | | `data[].balance_staked_alpha_as_tao` | `string` | Yes | Alpha-staked as TAO equivalent (RAO, u64 as string). | | `data[].balance_staked_root` | `string` | Yes | Root-staked (RAO, u64 as string). | | `data[].balance_total` | `string` | Yes | Total balance (RAO, u64 as string). | | `data[].block_number` | `integer (int32)` | Yes | Block number of the snapshot. | | `data[].created_on_date` | `string, nullable` | Yes | Account creation date (YYYY-MM-DD) from `account_created_at_v1`. `null` only when the account has no created-at row. | | `data[].created_on_network` | `string, nullable` | Yes | Network the account was created on (from `account_created_at_v1`). `null` only when the account has no created-at row. | | `data[].network` | `string` | Yes | Network name: `"finney"`, except on history rows, which carry their own network (`kusanagi` or `nakamoto` rows come back for those networks and for `network=all` or no `network`). See module docs. | | `data[].rank` | `integer (int32)` | Yes | Leaderboard rank at this snapshot. | | `data[].root_basket_claimable_tao` | `string, nullable` | Yes | TAO claimable from this coldkey's root beta baskets at this snapshot (RAO, u64 as string) — what redeeming every basket share would have realized at that block. `null` means **unknown**, never zero. It is populated on `/v1/accounts/{address}/history` from the daily snapshot block, and is `null` on rows before basket state existed on chain (below block 8,765,684, i.e. before 2026-08-03). On `/v1/accounts/leaderboard` it is always `null`: the figure needs a per-coldkey computation the *latest* snapshot does not carry, and the old API is the same — it fills the field for one address, never for a list. **Not part of `balance_total`.** It is an entitlement held by the funds, not a balance the coldkey holds, so it cannot move the rank. | | `data[].root_claim_type` | `string, nullable` | Yes | Always `null`. The root-claim preference was deleted from the chain at spec 441 and is no longer served (#774, see module docs); the field is kept because the OLD API returns it as `null`. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp, e.g. `"2026-06-13T09:10:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Accounts Pending Coldkey Swaps All pending (announced, not-yet-executed) coldkey swaps, read live from chain. _Source: https://taostats.io/docs/new/accounts/get-accounts-pending-coldkey-swaps_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/accounts/pending-coldkey-swaps ``` Requires an API key in the `Authorization` header. All pending (announced, not-yet-executed) coldkey swaps, read live from chain. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/accounts/pending-coldkey-swaps" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/accounts/pending-coldkey-swaps', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/accounts/pending-coldkey-swaps", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — All pending coldkey swaps | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block the swap was *requested* (announced), recovered as `execution_block − ColdkeySwapAnnouncementDelay` (the chain stores only the execution block `when = request + delay`). | | `data[].disputed` | `boolean` | Yes | Whether this swap is currently *disputed* — i.e. the announcer's coldkey is present in `SubtensorModule::ColdkeySwapDisputes`. A disputed swap is frozen (it cannot execute until the triumvirate resolves it) but remains genuinely pending, so it is flagged here, never excluded. | | `data[].disputed_block_number` | `integer (int32), nullable` | Yes | Block the dispute was raised (`ColdkeySwapDisputes` value), or `null` when the swap is not disputed. | | `data[].execution_block_number` | `integer (int32)` | Yes | Earliest block the swap can execute (`when`), read verbatim from chain storage. The swap does not auto-execute at this height — it becomes executable from here on, so this may already be at or below the head. | | `data[].new_coldkey` | `string, nullable` | Yes | New coldkey (SS58). Always `null` while pending: only its hash is committed on-chain until the swap is revealed/executed. | | `data[].new_coldkey_hash` | `string, nullable` | Yes | Hash of the new coldkey (0x-hex), as committed in the announcement. | | `data[].old_coldkey` | `string` | Yes | Current (old) coldkey (SS58) — the announcer. | | `data[].predicted_execution_timestamp` | `string` | Yes | Earliest-execution time (ISO 8601) for `execution_block_number`. Once that block has been produced and indexed this is its real `block_v1` timestamp — the moment the swap became executable — and it agrees with `/v1/blocks`. While the block is still ahead of the chain the time genuinely is not known yet, and only then is it projected as head time + (when − head) × 12s. That remaining projection is what the `predicted_` in the field name refers to. | | `data[].timestamp` | `string` | Yes | Request time (ISO 8601): the real chain timestamp of `block_number`, read from `block_v1`, so it agrees exactly with what `/v1/blocks` serves for that block. The request block is always in the past — it is recovered from an announcement the chain has already accepted — so there is nothing here to predict. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `500` — Internal/chain error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `503` — The chain node's answer for the swap maps could not be verified, even after one retry; the message names the map | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Alpha Every Taostats API endpoint in the Alpha group, with its method and path. _Source: https://taostats.io/docs/new/alpha_ _Last reviewed: 2026-10-07_ 5 Alpha endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Alpha History](https://taostats.io/docs/new/alpha/get-alpha-history) | `GET` | `/v1/alpha/history` | | [Get Alpha Leaderboard](https://taostats.io/docs/new/alpha/get-alpha-leaderboard) | `GET` | `/v1/alpha/leaderboard` | | [Get Alpha Portfolio](https://taostats.io/docs/new/alpha/get-alpha-portfolio) | `GET` | `/v1/alpha/portfolio` | | [Get Alpha Root Claims](https://taostats.io/docs/new/alpha/get-alpha-root-claims) | `GET` | `/v1/alpha/root-claims` | | [Get Alpha Hotkey Shares](https://taostats.io/docs/new/alpha/get-alpha-hotkey-shares) | `GET` | `/v1/alpha/hotkey-shares` | --- # Get Alpha History Daily-snapshot history of one alpha position. _Source: https://taostats.io/docs/new/alpha/get-alpha-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/alpha/history ``` Requires an API key in the `Authorization` header. Daily-snapshot history of one alpha position. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/alpha/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/alpha/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/alpha/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | Yes | Coldkey address (SS58 or 0x-hex; normalized to SS58). Required. | | `hotkey` | query | `string` | Yes | Hotkey address (SS58 or 0x-hex; normalized to SS58). Required. | | `netuid` | query | `integer (int32)` | Yes | Subnet UID. Required. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated daily history of one alpha stake position | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].balance` | `string` | Yes | Alpha balance (RAO) as a decimal string (u64). | | `data[].balance_as_tao` | `string` | Yes | TAO equivalent (RAO) as a decimal string (u64). | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].coldkey` | `string` | Yes | Staker coldkey, SS58 address. | | `data[].free_alpha` | `string` | Yes | Withdrawable part of `balance` (RAO, decimal string): `balance - locked_alpha`. | | `data[].global_rank` | `integer (int32), nullable` | Yes | Competition rank across all positions by `balance_as_tao` desc. `null` only for the (deferred) on-chain fetch; always present here. | | `data[].hotkey` | `string` | Yes | Validator hotkey, SS58 address. | | `data[].hotkey_name` | `string, nullable` | Yes | The hotkey's current identity name; `null` when its owner has none. | | `data[].locked_alpha` | `string` | Yes | Miner registration collateral locked on this position (RAO, decimal string). Part of `balance`, not extra — `locked_alpha + free_alpha == balance`. Zero unless the position holds collateral: from a registration on a subnet with a nonzero `CollateralLockShare`, or from a voluntary `add_collateral`, which any subnet accepts (spec 435). | | `data[].netuid` | `integer (int32)` | Yes | Subnet UID. | | `data[].subnet_rank` | `integer (int32), nullable` | Yes | Competition rank within the netuid by `balance` (alpha) desc. | | `data[].subnet_total_holders` | `integer (int32), nullable` | Yes | Number of emitted positions in the netuid. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid or missing query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Alpha Leaderboard Paginated leaderboard of alpha stake positions. _Source: https://taostats.io/docs/new/alpha/get-alpha-leaderboard_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/alpha/leaderboard ``` Requires an API key in the `Authorization` header. Paginated leaderboard of alpha stake positions. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/alpha/leaderboard" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/alpha/leaderboard', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/alpha/leaderboard", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Coldkey address (SS58 or 0x-hex; normalized to SS58). Exact match. | | `hotkey` | query | `string` | | Hotkey address (SS58 or 0x-hex; normalized to SS58). Exact match. | | `netuid` | query | `integer (int32)` | | Subnet UID. Exact match. | | `balance_min` | query | `integer (int64)` | | Minimum alpha balance (RAO, inclusive). | | `balance_max` | query | `integer (int64)` | | Maximum alpha balance (RAO, inclusive). | | `balance_as_tao_min` | query | `integer (int64)` | | Minimum alpha-as-TAO equivalent (RAO, inclusive). | | `balance_as_tao_max` | query | `integer (int64)` | | Maximum alpha-as-TAO equivalent (RAO, inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by: `global_rank` or `subnet_rank`. One of `global_rank`, `subnet_rank`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated leaderboard of alpha stake positions | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].balance` | `string` | Yes | Alpha balance (RAO) as a decimal string (u64). | | `data[].balance_as_tao` | `string` | Yes | TAO equivalent (RAO) as a decimal string (u64). | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot this row came from. | | `data[].coldkey` | `string` | Yes | Staker coldkey, SS58 address. | | `data[].free_alpha` | `string` | Yes | Withdrawable part of `balance` (RAO, decimal string): `balance - locked_alpha`. | | `data[].global_rank` | `integer (int32)` | Yes | Competition rank across all positions by `balance_as_tao` desc. | | `data[].hotkey` | `string` | Yes | Validator hotkey, SS58 address. | | `data[].hotkey_name` | `string, nullable` | Yes | The hotkey's current identity name: the on-chain identity of the coldkey the chain records as its owner today (`SubtensorModule::Owner`), the same name `/v1/validators` serves for a validator. Validator or not (#1314). `null` when the hotkey holds no stake at the current snapshot (so no stored owner) or its owner has no identity. | | `data[].locked_alpha` | `string` | Yes | Miner registration collateral locked on this position (RAO, decimal string). Part of `balance`, not extra — `locked_alpha + free_alpha == balance`. Zero unless the position holds collateral: from a registration on a subnet with a nonzero `CollateralLockShare`, or from a voluntary `add_collateral`, which any subnet accepts (spec 435). | | `data[].netuid` | `integer (int32)` | Yes | Subnet UID. | | `data[].subnet_rank` | `integer (int32)` | Yes | Competition rank within the netuid by `balance` (alpha) desc. | | `data[].subnet_total_holders` | `integer (int32)` | Yes | Number of emitted positions in the netuid. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Alpha Portfolio Coldkey alpha portfolio + trading aggregates. _Source: https://taostats.io/docs/new/alpha/get-alpha-portfolio_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/alpha/portfolio ``` Requires an API key in the `Authorization` header. Coldkey alpha portfolio + trading aggregates. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/alpha/portfolio" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/alpha/portfolio', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/alpha/portfolio", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | Yes | Staker coldkey (SS58 or 0x-hex; normalized to SS58). Required. | | `hotkey` | query | `string` | | Filter to one validator hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter to one subnet UID. | | `days` | query | `integer (int32)` | | Aggregate over the last N days only (omit for all-time). Must be \> 0. When set, `period_start_*` fields are populated. | ## Responses ### `200` — Coldkey alpha portfolio positions | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].average_purchase_price_tao` | `string` | Yes | Volume-weighted average buy price in TAO. | | `data[].average_purchase_price_usd` | `string, nullable` | Yes | Average buy price in USD. `null` — pending oracle. | | `data[].average_sale_price_tao` | `string` | Yes | Volume-weighted average sell price in TAO. | | `data[].average_sale_price_usd` | `string, nullable` | Yes | Average sell price in USD. `null` — pending oracle. | | `data[].balance` | `string` | Yes | Current alpha balance (RAO). | | `data[].balance_as_tao` | `string` | Yes | Current alpha as TAO equivalent (RAO). | | `data[].block_number` | `integer (int32)` | Yes | The block every figure in this row is true at, except `hotkey_name`, `subnet_rank` and `subnet_total_holders`: the head block the balance and the alpha price were read from the chain at, the newest finalized block the indexes have reached. Trades, transfers and moves are counted up to and including it, and TAO/USD is the rate in force at its time. The same for every row of a response. | | `data[].coldkey` | `string` | Yes | Staker coldkey, SS58. | | `data[].current_market_price_tao` | `string` | Yes | Current alpha price in TAO. | | `data[].current_market_price_usd` | `string, nullable` | Yes | Current alpha price in USD. `null` — pending oracle. | | `data[].hotkey` | `string` | Yes | Validator hotkey, SS58. | | `data[].hotkey_name` | `string, nullable` | Yes | The hotkey's current identity name: the on-chain identity of the coldkey the chain records as its owner today (`SubtensorModule::Owner`), the same name `/v1/validators` serves for a validator. Validator or not (#1314). `null` when the hotkey holds no stake at the current snapshot (so no stored owner) or its owner has no identity. | | `data[].netuid` | `integer (int32)` | Yes | Subnet UID. | | `data[].period_start_alpha` | `string, nullable` | Yes | Alpha balance at period start (RAO). Only when `days` is set. The balance at the last block before the window starts, as the OLD API serves it: for `days >= 8` the end-of-day snapshot in `stake_balance_history_v1` (`build_start_sql`), for `days < 8` the chain at the last block before the hour (`fetch_chain_start_rows`). | | `data[].period_start_alpha_cost_in_usd` | `string, nullable` | Yes | Alpha cost in USD at period start. `null` — pending oracle. | | `data[].period_start_alpha_price_in_tao` | `string, nullable` | Yes | Alpha price in TAO at period start. Only when `days` is set. | | `data[].period_start_tao_price_in_usd` | `string, nullable` | Yes | TAO price in USD at period start. `null` (only set when `days`, and pending oracle). | | `data[].realised_profit_tao` | `string` | Yes | Realised profit in TAO (RAO). | | `data[].realised_profit_usd` | `string, nullable` | Yes | Realised profit in USD. `null` — pending oracle. | | `data[].subnet_rank` | `integer (int32), nullable` | Yes | Rank within the subnet by balance, from the newest stake-balance snapshot (about hourly), not from `block_number`. `null` for positions with no current balance (traded-out within the window), and for one opened since that snapshot. | | `data[].subnet_total_holders` | `integer (int32), nullable` | Yes | Number of holders in the subnet. `null` as for `subnet_rank`. | | `data[].timestamp` | `string` | Yes | That block's time, ISO 8601 with millisecond precision. | | `data[].total_bought_alpha` | `string` | Yes | Total alpha bought (RAO). | | `data[].total_bought_alpha_as_tao` | `string` | Yes | Total bought, as TAO paid (RAO). | | `data[].total_bought_alpha_as_usd` | `string, nullable` | Yes | Total bought, as USD. `null` — pending oracle. | | `data[].total_buys` | `integer (int32)` | Yes | Number of buy transactions. | | `data[].total_earned_alpha` | `string` | Yes | Total alpha earned (emissions etc.): balance − bought − start − in + sold + out, where in and out also hold stake moved by a coldkey swap (RAO; may be negative). | | `data[].total_earned_alpha_as_tao` | `string` | Yes | Earned, as TAO at the current price (RAO). | | `data[].total_earned_alpha_as_usd` | `string, nullable` | Yes | Earned, as USD. `null` — pending oracle. | | `data[].total_sells` | `integer (int32)` | Yes | Number of sell transactions. | | `data[].total_sold_alpha` | `string` | Yes | Total alpha sold (RAO). | | `data[].total_sold_alpha_as_tao` | `string` | Yes | Total sold, as TAO received (RAO). | | `data[].total_sold_alpha_as_usd` | `string, nullable` | Yes | Total sold, as USD. `null` — pending oracle. | | `data[].total_transferred_in_alpha` | `string` | Yes | Total alpha transferred in (RAO). | | `data[].total_transferred_in_alpha_as_tao` | `string` | Yes | Transferred in, as TAO (RAO). | | `data[].total_transferred_in_alpha_as_usd` | `string, nullable` | Yes | Transferred in, as USD. `null` — pending oracle. | | `data[].total_transferred_out_alpha` | `string` | Yes | Total alpha transferred out (RAO). | | `data[].total_transferred_out_alpha_as_tao` | `string` | Yes | Transferred out, as TAO (RAO). | | `data[].total_transferred_out_alpha_as_usd` | `string, nullable` | Yes | Transferred out, as USD. `null` — pending oracle. | | `data[].total_transfers_in` | `integer (int32)` | Yes | Number of inbound transfers. | | `data[].total_transfers_out` | `integer (int32)` | Yes | Number of outbound transfers. | | `data[].unrealised_profit_tao` | `string` | Yes | Unrealised profit in TAO (RAO). | | `data[].unrealised_profit_usd` | `string, nullable` | Yes | Unrealised profit in USD. `null` — pending oracle. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Alpha Root Claims Paginated root-claim event history. _Source: https://taostats.io/docs/new/alpha/get-alpha-root-claims_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/alpha/root-claims ``` Requires an API key in the `Authorization` header. Paginated root-claim event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/alpha/root-claims" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/alpha/root-claims', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/alpha/root-claims", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Coldkey address (SS58 or 0x-hex; normalized to SS58). Exact match. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of root-claim events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height the claim event was emitted in. | | `data[].coldkey` | `string` | Yes | Claiming coldkey, SS58 address. | | `data[].extrinsic_id` | `string, nullable` | Yes | Parent extrinsic ID for manual `claim_root` calls; `null` for per-block auto-claim events. | | `data[].tao` | `string, nullable` | Yes | Total TAO realised by the claim, in rao, as a decimal string. `null` for claims below block 8,765,684, where the chain did not emit an amount at all. A claim that realised nothing is `"0"`, not `null` — the two are different facts. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Alpha Hotkey Shares Paginated hotkey alpha and shares per subnet. _Source: https://taostats.io/docs/new/alpha/get-alpha-hotkey-shares_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/alpha/hotkey-shares ``` Requires an API key in the `Authorization` header. Paginated hotkey alpha and shares per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/alpha/hotkey-shares" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/alpha/hotkey-shares', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/alpha/hotkey-shares", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Subnet UID. Exact match. | | `hotkey` | query | `string` | | Hotkey address (SS58 or 0x-hex; normalized to SS58). Exact match. | | `alpha_min` | query | `integer (int64)` | | Minimum alpha (RAO, inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | ## Responses ### `200` — Paginated hotkey alpha and shares per subnet, largest alpha first | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha` | `string` | Yes | The hotkey's `TotalHotkeyAlpha` on this subnet (RAO, u64 as a decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block of the snapshot this row was read at. Every field of the row is true at this block. | | `data[].hotkey` | `string` | Yes | Hotkey, SS58 address. | | `data[].netuid` | `integer (int32)` | Yes | Subnet UID. | | `data[].shares` | `string` | Yes | The hotkey's share-pool denominator on this subnet, as the chain holds it (`TotalHotkeyShares`, else `TotalHotkeySharesV2`). A decimal string, because it is fixed-point with more precision than a float carries. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp of that block, millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Chain Every Taostats API endpoint in the Chain group, with its method and path. _Source: https://taostats.io/docs/new/chain_ _Last reviewed: 2026-10-07_ 5 Chain endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Blocks](https://taostats.io/docs/new/chain/get-blocks) | `GET` | `/v1/blocks` | | [Get Calls](https://taostats.io/docs/new/chain/get-calls) | `GET` | `/v1/calls` | | [Get Events](https://taostats.io/docs/new/chain/get-events) | `GET` | `/v1/events` | | [Get Extrinsics](https://taostats.io/docs/new/chain/get-extrinsics) | `GET` | `/v1/extrinsics` | | [Get Proxy Calls](https://taostats.io/docs/new/chain/get-proxy-calls) | `GET` | `/v1/proxy-calls` | --- # Get Blocks Paginated list of indexed blocks. _Source: https://taostats.io/docs/new/chain/get-blocks_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/blocks ``` Requires an API key in the `Authorization` header. Paginated list of indexed blocks. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/blocks" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/blocks', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/blocks", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `hash` | query | `string` | | Exact block hash (0x-prefixed hex). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of blocks matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].calls_count` | `integer (int32)` | Yes | Total calls in the block (including children of `batch`/`proxy`/etc.). | | `data[].events_count` | `integer (int32)` | Yes | Total events emitted in the block. | | `data[].extrinsics_count` | `integer (int32)` | Yes | Total extrinsics in the block. | | `data[].extrinsics_root` | `string` | Yes | Extrinsics trie root hash (0x-prefixed hex). | | `data[].hash` | `string` | Yes | Block hash (0x-prefixed hex). | | `data[].impl_name` | `string` | Yes | Implementation name reported by the runtime. | | `data[].impl_version` | `integer (int32)` | Yes | Implementation version reported by the runtime. | | `data[].parent_hash` | `string` | Yes | Parent block hash (0x-prefixed hex). | | `data[].spec_name` | `string` | Yes | Runtime spec name (e.g. `node-subtensor`). | | `data[].spec_version` | `integer (int32)` | Yes | Runtime spec version active at this block. | | `data[].state_root` | `string` | Yes | State trie root hash (0x-prefixed hex). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `data[].validator` | `string, nullable` | Yes | SS58 block author. `null` when the producer didn't decode an author (e.g. very early blocks or a header without `PreRuntime`/`Seal` consensus digests). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Calls Paginated list of indexed calls (root + nested). _Source: https://taostats.io/docs/new/chain/get-calls_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/calls ``` Requires an API key in the `Authorization` header. Paginated list of indexed calls (root + nested). ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/calls" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/calls', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/calls", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `full_name` | query | `string` | | Exact match on `full_name` (`Pallet.Name`). | | `pallet` | query | `string` | | Exact match on `pallet`. | | `name` | query | `string` | | Exact match on `name`. | | `id` | query | `string` | | Exact match on `id`. Format: `{block}-{ext_idx:04}[-N[-N...]]`. | | `parent_id` | query | `string` | | Exact match on `parent_id`. The only `parent_id` filter ever applied to `/v1/calls` — never injected by the handler. | | `success` | query | `boolean` | | Exact match on `success`. | | `signer_address` | query | `string` | | Extrinsic signer address. Accepts SS58 (`5G...`) or 0x-hex (`0xd435...`); normalized to SS58 before the query. Matches every call in a signed extrinsic. | | `origin_address` | query | `string` | | Call origin address. Accepts SS58 or 0x-hex; normalized to SS58 before the query. Matches this call's `RuntimeOrigin` account. | | `extrinsic_id` | query | `string` | | Exact match on `extrinsic_id`. Use to fetch every call (root + nested) under a given extrinsic. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `include_args` | query | `boolean` | | Include the heavy `args` column. `args` is **omitted by default** on this listing endpoint. `true` forces it present (regardless of `fields`); `false` forces it omitted. When unset, `args` appears only if `fields` lists it, or if `id` targets a single row. | | `fields` | query | `string` | | Sparse fieldset: comma-separated list of top-level response keys to return (e.g. `fields=id,pallet,name`). Unknown names are ignored. `args_summary` is always returned regardless. When omitted, the full default field set is returned. | ## Responses ### `200` — Paginated list of calls (root + nested) matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].args` | `object` | | Call arguments as a structured JSON value. Parsed from the JSON string the indexer writes into the `args` column. `null` when the indexer didn't record args. **Omitted by default** on this listing endpoint to keep payloads small (a `Sudo.sudo_unchecked_weight` wrapping `System.set_code` inlines an 8.78 MB WASM blob). Opt back in with `?include_args=true`, by selecting it via `?fields=args`, or by targeting a single row with `id=` (the detail path). When omitted the column is not read from ClickHouse at all. The derived `args_summary` is always present. Schema-optional (not in `required`): unlike other nullable fields, `args` may be **absent** from a response — that is the #41 default — so SDKs must model it as optional, not present-but-null. | | `data[].args_summary` | `object` | Yes | Always-present (~100 B) summary of the call's single inner/wrapped call. The `inner_call*` sub-fields are `null` when the row has no inner call. Lets a client flag a runtime upgrade, and size it, without fetching the blob. See [`ArgsSummary`]. | | `data[].args_summary.args_size_bytes` | `integer (int64)` | Yes | Exact byte length of the row's whole stored `args` column — always present, always exact, and cheap to read (`length(args)` never transfers the value). `0` when the row has no args. | | `data[].args_summary.inner_call` | `string, nullable` | Yes | Inner call's full name (`Pallet.Name`, e.g. `"System.set_code"`), or `null` when the row has no inner call. | | `data[].args_summary.inner_call_kind` | `string, nullable` | Yes | Coarse classification of the inner call, or `null` when the row has no inner call. One of `runtime_upgrade`, `wrapped_call`. | | `data[].args_summary.inner_call_size_bytes` | `integer (int64), nullable` | Yes | Byte length of the inner call's JSON-encoded args (the heavy payload). `null` when the row has no inner call, **and** when the row's `args` did not fit in the [`ARGS_HEAD_CHARS`] prefix the listing reads and the caller did not request the column: measuring it would mean reading a multi-MiB blob, which is the read that killed the pods (#574). `args_size_bytes` carries the magnitude in that case, and `?include_args=true` (or `id=`) makes this field exact again. | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].error` | `object` | Yes | Error details (JSON) if the call failed. Root calls carry `System.ExtrinsicFailed`; child calls carry the boundary-event error. | | `data[].extrinsic_id` | `string` | Yes | Parent extrinsic ID (the root call's `id`). Same value as `id` on root rows. | | `data[].extrinsic_index` | `integer (int32)` | Yes | Extrinsic index within the block. | | `data[].id` | `string` | Yes | Call ID. Format: `{block}-{ext_idx:04}[-N[-N...]]`. | | `data[].name` | `string` | Yes | | | `data[].origin` | `object` | Yes | `RuntimeOrigin` JSON. Populated on root rows; `null` on children. | | `data[].origin_address` | `string, nullable` | Yes | SS58 address of this call's `RuntimeOrigin` account, set by the immediate parent wrapper. Equal to `signer_address` on the root of a signed extrinsic; on children follows the wrapper (proxied `real`, `sudo_as` `who`, etc.). | | `data[].pallet` | `string` | Yes | | | `data[].parent_id` | `string, nullable` | Yes | Parent call ID, or `null` on root calls. | | `data[].signer_address` | `string, nullable` | Yes | SS58 address of the extrinsic signer — same on every call in a signed extrinsic; `null` for unsigned extrinsics. | | `data[].success` | `boolean` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — The selected rows carry more `args` than one request may materialize — lower `limit`, narrow the filter, or drop `include_args` | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `503` — Another large `args` response is already in flight; retry shortly | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Events Paginated list of indexed events. _Source: https://taostats.io/docs/new/chain/get-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/events ``` Requires an API key in the `Authorization` header. Paginated list of indexed events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `pallet` | query | `string` | | Filter by pallet name (exact match). | | `name` | query | `string` | | Filter by event name (exact match). | | `full_name` | query | `string` | | Filter by `Pallet.Name` (exact match). | | `id` | query | `string` | | Event ID (`{block_number}-{event_idx:04}`) — exact match. | | `phase` | query | `string` | | Filter by phase. Typical values: `Initialization`, `Finalization`, `ApplyExtrinsic`. No allow-list enforced — unknown values return zero rows. | | `call_id` | query | `string` | | Filter by call ID (exact match). Returns only events that have been mapped to a call. | | `extrinsic_id` | query | `string` | | Filter by extrinsic ID (`{block_number}-{extrinsic_idx:04}`) — exact match. Returns only events under that extrinsic; excludes Initialization / Finalization-phase events with `extrinsic_id = null`. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of events matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].args` | `object` | Yes | Event arguments as a structured JSON value. Parsed from the JSON string the indexer writes into the `args` column. `null` when the indexer wrote nothing (no args) or when the stored string failed to parse. The shape is intentionally any-value: the substrate-archive decoder produces an `object` for named-field composites, an `array` for tuple-style unnamed composites, and a `string` for empty variants (`composite_inner_to_json` in `substrate-archive/src/decode.rs`). Advertising the schema as `object`-only would make generated SDKs reject valid rows. utoipa 5's built-in `ToSchema for serde_json::Value` emits `SchemaType::AnyValue` (no `type` constraint), which matches the contract. `#[schema(required)]` keeps the field always present in responses — when the row's `args` is None, the JSON value is the explicit literal `null` rather than an omitted field. | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].call_id` | `string, nullable` | Yes | Associated call ID; `null` when the event hasn't been mapped to a call. | | `data[].extrinsic_id` | `string, nullable` | Yes | Parent extrinsic ID; `null` for Initialization / Finalization phase events. | | `data[].id` | `string` | Yes | `{block_number}-{event_idx:04}` (event_idx is the global index within the block, across all phases). | | `data[].index` | `integer (int32)` | Yes | Event index within the block (global across phases). | | `data[].name` | `string` | Yes | Event name. | | `data[].pallet` | `string` | Yes | Pallet name. | | `data[].phase` | `string` | Yes | Event phase: `Initialization`, `Finalization`, or `ApplyExtrinsic`. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — The selected rows carry more `args` than one request may materialize — lower `limit` or narrow the filter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `503` — Another large `args` response is already in flight; retry shortly | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Extrinsics Paginated list of root extrinsics from call_v1. _Source: https://taostats.io/docs/new/chain/get-extrinsics_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/extrinsics ``` Requires an API key in the `Authorization` header. Paginated list of root extrinsics from `call_v1`. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/extrinsics" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/extrinsics', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/extrinsics", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. `testnet` is not accepted — testnet has its own family of tables and is not a value of the mainnet `network` column. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `hash` | query | `string` | | Exact extrinsic hash (0x-prefixed hex). | | `full_name` | query | `string` | | Exact `Pallet.Name` match. | | `pallet` | query | `string` | | Exact pallet match. | | `name` | query | `string` | | Exact call-name match. | | `id` | query | `string` | | Exact extrinsic ID match (`{block_number}-{ext_idx:04}`). | | `success` | query | `boolean` | | Filter by success status. | | `signer_address` | query | `string` | | Signer address. Accepts SS58 (`5G...`) or 0x-hex (`0xabcd...`); normalized to SS58 before the query. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `include_args` | query | `boolean` | | Include the heavy `args` column. `args` is **omitted by default** on this listing endpoint. `true` forces it present (regardless of `fields`); `false` forces it omitted. When unset, `args` appears only if `fields` lists it, or if `id` targets a single row. | | `fields` | query | `string` | | Sparse fieldset: comma-separated list of top-level response keys to return (e.g. `fields=id,pallet,name`). Unknown names are ignored. `args_summary` is always returned regardless. When omitted, the full default field set is returned. | ## Responses ### `200` — Paginated list of root extrinsics matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].args` | `object` | | Call arguments as an arbitrary JSON value (typically an object keyed by argument name; may be `null` when the stored args blob is absent or fails to parse). **Omitted by default** on this listing endpoint to keep payloads small (a `Sudo.sudo_unchecked_weight` wrapping `System.set_code` inlines an 8.78 MB WASM blob). Opt back in with `?include_args=true`, by selecting it via `?fields=args`, or by targeting a single row with `id=` (the detail path). When omitted the column is not read from ClickHouse at all. The derived `args_summary` is always present. Schema-optional (not in `required`): unlike other nullable fields, `args` may be **absent** from a response — that is the #41 default — so SDKs must model it as optional, not present-but-null. | | `data[].args_summary` | `object` | Yes | Always-present (~80 B) summary of the extrinsic's single inner/wrapped call. All three sub-fields are `null` when the row has no inner call. Lets a client flag a runtime upgrade without fetching the blob. See [`ArgsSummary`]. | | `data[].args_summary.args_size_bytes` | `integer (int64)` | Yes | Exact byte length of the row's whole stored `args` column — always present, always exact, and cheap to read (`length(args)` never transfers the value). `0` when the row has no args. | | `data[].args_summary.inner_call` | `string, nullable` | Yes | Inner call's full name (`Pallet.Name`, e.g. `"System.set_code"`), or `null` when the row has no inner call. | | `data[].args_summary.inner_call_kind` | `string, nullable` | Yes | Coarse classification of the inner call, or `null` when the row has no inner call. One of `runtime_upgrade`, `wrapped_call`. | | `data[].args_summary.inner_call_size_bytes` | `integer (int64), nullable` | Yes | Byte length of the inner call's JSON-encoded args (the heavy payload). `null` when the row has no inner call, **and** when the row's `args` did not fit in the [`ARGS_HEAD_CHARS`] prefix the listing reads and the caller did not request the column: measuring it would mean reading a multi-MiB blob, which is the read that killed the pods (#574). `args_size_bytes` carries the magnitude in that case, and `?include_args=true` (or `id=`) makes this field exact again. | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].error` | `object` | Yes | Error details when `success = false`, as an arbitrary JSON value (typically an object with `pallet` / `name` / `docs` fields). `null` on success or when no error detail is recorded. | | `data[].fee` | `string, nullable` | Yes | Transaction fee as a decimal string (`u64`). `null` only if the underlying row is missing the field (a root-row anomaly). | | `data[].fee_payer` | `string, nullable` | Yes | SS58 address that actually paid the fee. Differs from `signer_address` when proxy `RealPaysFee` is active (spec 385+). `null` only if the underlying row is missing the field. | | `data[].hash` | `string` | Yes | Extrinsic hash (0x-prefixed hex). | | `data[].id` | `string` | Yes | Extrinsic ID — `{block_number}-{extrinsic_index:04}` on root rows. | | `data[].index` | `integer (int32)` | Yes | Extrinsic index within the block (equals `extrinsic_index` on the root call). | | `data[].name` | `string` | Yes | Call name within the pallet. | | `data[].pallet` | `string` | Yes | Pallet name. | | `data[].signature` | `object` | Yes | Signature data as an arbitrary JSON value (sourced from `CallRow::origin`). For signed extrinsics this is typically a `"0x..."` hex string (the encoded signature bytes); for `RuntimeOrigin` wrappers (`Sudo`, `Multisig`, etc.) it can be an object. `null` when the extrinsic is unsigned or when the stored origin blob fails to parse. SDK consumers should model this as `unknown`/`any JSON`. | | `data[].signer_address` | `string, nullable` | Yes | SS58 signer of the extrinsic. `null` for unsigned extrinsics. | | `data[].success` | `boolean` | Yes | Whether the extrinsic succeeded. | | `data[].timestamp` | `string` | Yes | ISO-8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `data[].tip` | `string, nullable` | Yes | Tip amount as a decimal string (`u64`). `null` only if the underlying row is missing the field (a root-row anomaly). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — The selected rows carry more `args` than one request may materialize — lower `limit`, narrow the filter, or drop `include_args` | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `503` — Another large `args` response is already in flight; retry shortly | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Proxy Calls Paginated list of indexed proxy calls. _Source: https://taostats.io/docs/new/chain/get-proxy-calls_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/proxy-calls ``` Requires an API key in the `Authorization` header. Paginated list of indexed proxy calls. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/proxy-calls" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/proxy-calls', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/proxy-calls", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `id` | query | `string` | | Exact proxy call ID. | | `signer_address` | query | `string` | | Signer address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `real_address` | query | `string` | | Real (proxied) address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `extrinsic_hash` | query | `string` | | Exact extrinsic hash (0x-prefixed hex). | | `extrinsic_id` | query | `string` | | Exact extrinsic ID (e.g. `5000000-0002`). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of proxy calls matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].args` | `object` | Yes | Inner dispatched call arguments as a structured JSON value. Parsed from the JSON string the indexer writes into the `args` column. The shape varies by dispatched call, so callers should treat it as opaque JSON. Serializes as the explicit literal `null` if the stored string ever fails to parse. utoipa 5's built-in `ToSchema for serde_json::Value` emits `SchemaType::AnyValue` (no `type` constraint), matching the real-world variability. `#[schema(required)]` keeps the field always present in responses. | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].extrinsic_hash` | `string` | Yes | Extrinsic hash (0x-prefixed hex). | | `data[].extrinsic_id` | `string` | Yes | Parent extrinsic ID. | | `data[].id` | `string` | Yes | Proxy call ID. | | `data[].network` | `string` | Yes | Network name. | | `data[].real_address` | `string` | Yes | Real (proxied) SS58 address. | | `data[].signer_address` | `string` | Yes | Signer SS58 address. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — The selected rows carry more `args` than one request may materialize — lower `limit` or narrow the filter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `503` — Another large `args` response is already in flight; retry shortly | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # CoinMarketCap Every Taostats API endpoint in the CoinMarketCap group, with its method and path. _Source: https://taostats.io/docs/new/cmc_ _Last reviewed: 2026-10-07_ 7 CoinMarketCap endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Cmc Assets](https://taostats.io/docs/new/cmc/get-cmc-assets) | `GET` | `/v1/cmc/assets` | | [Get Cmc Circulating Supply](https://taostats.io/docs/new/cmc/get-cmc-circulating-supply) | `GET` | `/v1/cmc/circulating-supply` | | [Get Cmc Order Book Market Pair](https://taostats.io/docs/new/cmc/get-cmc-order-book-market-pair) | `GET` | `/v1/cmc/order-book/market-pair` | | [Get Cmc Summary](https://taostats.io/docs/new/cmc/get-cmc-summary) | `GET` | `/v1/cmc/summary` | | [Get Cmc Ticker](https://taostats.io/docs/new/cmc/get-cmc-ticker) | `GET` | `/v1/cmc/ticker` | | [Get Cmc Total Supply](https://taostats.io/docs/new/cmc/get-cmc-total-supply) | `GET` | `/v1/cmc/total-supply` | | [Get Cmc Trades Market Pair](https://taostats.io/docs/new/cmc/get-cmc-trades-market-pair) | `GET` | `/v1/cmc/trades/market-pair` | --- # Get Cmc Assets Returns a bare JSON array of single-key objects. _Source: https://taostats.io/docs/new/cmc/get-cmc-assets_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/assets ``` Requires an API key in the `Authorization` header. Returns a bare JSON array of single-key objects. Each element is `{"SN{netuid}": { ...asset fields... }}`, matching the legacy `Vec` where each response wraps a flattened `HashMap`. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/assets" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/assets', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/assets", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — All tradeable subnet assets | Field | Type | Required | | --- | --- | --- | | `[]` | `object` | | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Circulating Supply GET /v1/cmc/circulating-supply — Taostats API endpoint in the CoinMarketCap group. _Source: https://taostats.io/docs/new/cmc/get-cmc-circulating-supply_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/circulating-supply ``` Requires an API key in the `Authorization` header. Bare JSON number. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/circulating-supply" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/circulating-supply', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/circulating-supply", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Subnet ID. Omit (or `0`) for TAO. Negative or absent is treated as TAO, matching the legacy `unwrap_or(0)` + `if netuid > 0` branch. | ## Responses ### `200` — Circulating supply in whole tokens (bare JSON number) Returns `text/plain` — a `number (double)` body. ### `404` — Subnet not found / no issuance data (legacy plain-text body `Netuid not found`) Returns `text/plain` — a `string` body. ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Order Book Market Pair Always an empty array. _Source: https://taostats.io/docs/new/cmc/get-cmc-order-book-market-pair_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/order-book/market-pair ``` Requires an API key in the `Authorization` header. Always an empty array. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/order-book/market-pair" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/order-book/market-pair', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/order-book/market-pair", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `market_pair` | query | `string` | | Market pair identifier. Accepted for CMC-format compatibility but unused (there is no on-chain order book). Optional — matching the legacy handler, which ignores query params entirely and never 400s. | ## Responses ### `200` — Order book (always empty) | Field | Type | Required | Description | | --- | --- | --- | --- | | `[].name` | `string` | Yes | Market pair name. | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Summary Market summary for all active trading pairs. _Source: https://taostats.io/docs/new/cmc/get-cmc-summary_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/summary ``` Requires an API key in the `Authorization` header. Market summary for all active trading pairs. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/summary" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/summary', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/summary", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Market summary for all trading pairs | Field | Type | Required | Description | | --- | --- | --- | --- | | `[].base_currency` | `string` | Yes | Base currency symbol (`"TAO"` always). | | `[].base_volume` | `string` | Yes | 24h TAO volume in whole TAO (decimal string). Trades only: stake transfers and validator-to-validator stake moves are not counted. | | `[].highest_bid` | `string` | Yes | Highest bid — pinned to `last_price` (no order book). | | `[].highest_price_24h` | `string` | Yes | 24h high price, or current pool price when no trades (decimal string). | | `[].last_price` | `string` | Yes | Last traded price in the 24h window, or current pool price (decimal string). | | `[].lowest_ask` | `string` | Yes | Lowest ask — pinned to `last_price` (no order book). | | `[].lowest_price_24h` | `string` | Yes | 24h low price, or current pool price when no trades (decimal string). | | `[].price_change_percent_24h` | `string` | Yes | 24h price change percent (decimal string; `"0"` when unavailable). | | `[].quote_currency` | `string` | Yes | Quote currency symbol, e.g. `"SN1"`. | | `[].quote_volume` | `string` | Yes | 24h alpha volume in whole alpha (decimal string). Trades only, as `base_volume`. | | `[].trading_pairs` | `string` | Yes | Trading pair identifier, e.g. `"TAO_SN1"`. | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Ticker Ticker for all active trading pairs. _Source: https://taostats.io/docs/new/cmc/get-cmc-ticker_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/ticker ``` Requires an API key in the `Authorization` header. Ticker for all active trading pairs. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/ticker" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/ticker', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/ticker", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Ticker for all active trading pairs | Field | Type | Required | | --- | --- | --- | | `[]` | `object` | | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Total Supply Returns a bare JSON number. _Source: https://taostats.io/docs/new/cmc/get-cmc-total-supply_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/total-supply ``` Requires an API key in the `Authorization` header. Returns a bare JSON number. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/total-supply" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/total-supply', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/total-supply", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Subnet ID. Omit (or `0`) for TAO. Negative or absent is treated as TAO, matching the legacy `unwrap_or(0)` + `if netuid > 0` branch. | ## Responses ### `200` — Total supply in whole tokens (bare JSON number) Returns `text/plain` — a `number (double)` body. ### `404` — Subnet not found (legacy plain-text body `Netuid not found`) Returns `text/plain` — a `string` body. ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Cmc Trades Market Pair Recent (last 5 min) trades for a pair. _Source: https://taostats.io/docs/new/cmc/get-cmc-trades-market-pair_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/cmc/trades/market-pair ``` Requires an API key in the `Authorization` header. Recent (last 5 min) trades for a pair. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/cmc/trades/market-pair" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/cmc/trades/market-pair', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/cmc/trades/market-pair", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `market_pair` | query | `string` | Yes | Market pair identifier, e.g. `SN0_SN1` or `TAO_SN1`. Both spellings are accepted here; accepting `TAO_SN` deliberately differs from the OLD API (#1561). | ## Responses ### `200` — Recent trades for the market pair | Field | Type | Required | Description | | --- | --- | --- | --- | | `[].base_volume` | `string` | Yes | Base (TAO) volume (decimal string). | | `[].price` | `string` | Yes | Trade price in TAO (decimal string) — the trade's average execution price, not the subnet pool's spot price (#984). | | `[].quote_volume` | `string` | Yes | Quote (alpha) volume (decimal string). | | `[].timestamp` | `integer (int32)` | Yes | Trade timestamp (unix seconds). | | `[].trade_id` | `integer (int64)` | Yes | Trade identifier (padded block number + event index). | | `[].type` | `string` | Yes | Trade side: `"Buy"` (stake) or `"Sell"` (unstake). | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # CoinGecko Every Taostats API endpoint in the CoinGecko group, with its method and path. _Source: https://taostats.io/docs/new/coingecko_ _Last reviewed: 2026-10-07_ 4 CoinGecko endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get CoinGecko Asset](https://taostats.io/docs/new/coingecko/get-coingecko-asset) | `GET` | `/v1/coingecko/asset` | | [Get CoinGecko Events](https://taostats.io/docs/new/coingecko/get-coingecko-events) | `GET` | `/v1/coingecko/events` | | [Get CoinGecko Latest Block](https://taostats.io/docs/new/coingecko/get-coingecko-latest-block) | `GET` | `/v1/coingecko/latest-block` | | [Get CoinGecko Pair](https://taostats.io/docs/new/coingecko/get-coingecko-pair) | `GET` | `/v1/coingecko/pair` | --- # Get CoinGecko Asset GET /v1/coingecko/asset — Taostats API endpoint in the CoinGecko group. _Source: https://taostats.io/docs/new/coingecko/get-coingecko-asset_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/coingecko/asset ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/coingecko/asset" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/coingecko/asset', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/coingecko/asset", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | query | `string` | Yes | Asset ID — the subnet netuid as a string (e.g. `"1"`). | ## Responses ### `200` — CoinGecko asset metadata | Field | Type | Required | Description | | --- | --- | --- | --- | | `asset` | `object` | Yes | The asset metadata object nested inside `CoinGeckoAssetResponse`. Field names and serde renames mirror the legacy `CoinGeckoAsset` struct exactly. Supply fields are decimal strings (e.g. `"12345.678901234"`), matching the legacy `rust_decimal::Decimal` serialisation output. | | `asset.circulatingSupply` | `string` | Yes | | | `asset.decimals` | `integer (int32)` | Yes | | | `asset.id` | `string` | Yes | | | `asset.maxSupply` | `string` | Yes | | | `asset.name` | `string` | Yes | | | `asset.symbol` | `string` | Yes | | | `asset.totalSupply` | `string` | Yes | | ### `400` — Invalid asset id | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — Asset not found | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get CoinGecko Events DTAO swap events within a block range. _Source: https://taostats.io/docs/new/coingecko/get-coingecko-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/coingecko/events ``` Requires an API key in the `Authorization` header. DTAO swap events within a block range. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/coingecko/events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/coingecko/events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/coingecko/events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `fromBlock` | query | `integer (int32)` | Yes | Start block (inclusive). | | `toBlock` | query | `integer (int32)` | Yes | End block (inclusive, at most 20 blocks from `fromBlock`). | ## Responses ### `200` — Swap events within the block range | Field | Type | Required | Description | | --- | --- | --- | --- | | `events` | `array` | Yes | | | `events[].asset0In` | `string, nullable` | Yes | | | `events[].asset0Out` | `string, nullable` | Yes | | | `events[].asset1In` | `string, nullable` | Yes | | | `events[].asset1Out` | `string, nullable` | Yes | | | `events[].block` | `object` | Yes | Block metadata for a CoinGecko event. | | `events[].block.blockNumber` | `integer (int32)` | Yes | | | `events[].block.blockTimestamp` | `integer (int64)` | Yes | | | `events[].eventIndex` | `integer (int32)` | Yes | | | `events[].eventType` | `string` | Yes | | | `events[].maker` | `string` | Yes | | | `events[].pairId` | `string` | Yes | | | `events[].priceNative` | `string` | Yes | | | `events[].reserves` | `object` | Yes | Pool reserves at the event's block, in whole tokens (decimal strings). | | `events[].reserves.asset0` | `string` | Yes | | | `events[].reserves.asset1` | `string` | Yes | | | `events[].txnId` | `string` | Yes | | | `events[].txnIndex` | `integer (int32)` | Yes | | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get CoinGecko Latest Block Latest indexed block. _Source: https://taostats.io/docs/new/coingecko/get-coingecko-latest-block_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/coingecko/latest-block ``` Requires an API key in the `Authorization` header. Latest indexed block. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/coingecko/latest-block" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/coingecko/latest-block', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/coingecko/latest-block", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest indexed block | Field | Type | Required | Description | | --- | --- | --- | --- | | `block` | `object` | Yes | CoinGecko latest-block descriptor. Field names are camelCase per the CoinGecko contract. | | `block.blockNumber` | `integer (int32)` | Yes | Block number. | | `block.blockTimestamp` | `integer (int64)` | Yes | Block timestamp in unix seconds. | ### `404` — No indexed block | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get CoinGecko Pair Single pair metadata for CoinGecko. _Source: https://taostats.io/docs/new/coingecko/get-coingecko-pair_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/coingecko/pair ``` Requires an API key in the `Authorization` header. Single pair metadata for CoinGecko. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/coingecko/pair" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/coingecko/pair', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/coingecko/pair", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | query | `string` | Yes | Pair id in the form `0-{netuid}` where `netuid >= 1` (e.g. `0-1`). | ## Responses ### `200` — CoinGecko pair metadata | Field | Type | Required | Description | | --- | --- | --- | --- | | `pair` | `object` | Yes | CoinGecko pair descriptor. Field names are camelCase per the CoinGecko contract. | | `pair.asset0Id` | `string` | Yes | Asset 0 id. Always `"0"` (TAO). | | `pair.asset1Id` | `string` | Yes | Asset 1 id — the subnet netuid as a string. | | `pair.dexKey` | `string` | Yes | DEX key. Always `"taostats"`. | | `pair.id` | `string` | Yes | Pair id, echoed from the request (`0-{netuid}`). | ### `400` — Invalid pair id | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — Pair not found | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Contract Events Every Taostats API endpoint in the Contract Events group, with its method and path. _Source: https://taostats.io/docs/new/contract-events_ _Last reviewed: 2026-10-07_ One Contract Events endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Get Contract Events](https://taostats.io/docs/new/contract-events/get-contract-events) | `GET` | `/v1/contract-events` | --- # Get Contract Events Paginated Contracts pallet events. _Source: https://taostats.io/docs/new/contract-events/get-contract-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/contract-events ``` Requires an API key in the `Authorization` header. Paginated Contracts pallet events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/contract-events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/contract-events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/contract-events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `id` | query | `string` | | One event by id, `{block_number}-{index:04}` (the OLD API's `/contract_event/{id}/v1`). An unknown id returns an empty page. | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block-number range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block-number range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `name` | query | `string` | | Filter by event name (exact), e.g. `ContractEmitted`. | | `topic_0` | query | `string` | | Filter by topic 0 (exact, `0x` hex). | | `topic_1` | query | `string` | | Filter by topic 1 (exact, `0x` hex). | | `topic_2` | query | `string` | | Filter by topic 2 (exact, `0x` hex). | | `topic_3` | query | `string` | | Filter by topic 3 (exact, `0x` hex). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. A page past the window is rejected with a 400. Reach deeper history with the range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Clamped into [1, 200], as the OLD API clamps rather than rejects. | | `order_by` | query | `string` | | `id` (default: block then index), `block_number`, `timestamp` or `name`. Ties are broken by block then index. | | `order_dir` | query | `string` | | Sort direction: `asc` or `desc`. Default: `desc`. | ## Responses ### `200` — Paginated list of contract events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].args` | `object` | Yes | The event's fields: account ids as `0x` public keys, bytes as `0x` hex, camelCase keys, balances as decimal strings. | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].id` | `string` | Yes | Event id, `{block_number}-{index:04}`. | | `data[].index` | `integer (int32)` | Yes | The event's index within its block. | | `data[].name` | `string` | Yes | Event name, e.g. `ContractEmitted`, `Called`. | | `data[].timestamp` | `string` | Yes | Block time, ISO 8601 to the second (e.g. `2026-05-02T23:57:12Z`), as the OLD API serves it. | | `data[].topic_0` | `string, nullable` | Yes | Topic 0; for `ContractEmitted`, the ink! event signature. | | `data[].topic_1` | `string, nullable` | Yes | | | `data[].topic_2` | `string, nullable` | Yes | | | `data[].topic_3` | `string, nullable` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # EVM Every Taostats API endpoint in the EVM group, with its method and path. _Source: https://taostats.io/docs/new/evm_ _Last reviewed: 2026-10-07_ 6 EVM endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get EVM Address From SS58](https://taostats.io/docs/new/evm/get-evm-address-from-ss58) | `GET` | `/v1/evm/address_from_ss58` | | [Get EVM Blocks](https://taostats.io/docs/new/evm/get-evm-blocks) | `GET` | `/v1/evm/blocks` | | [Get EVM Contracts](https://taostats.io/docs/new/evm/get-evm-contracts) | `GET` | `/v1/evm/contracts` | | [Get EVM Conversions SS58 To Address](https://taostats.io/docs/new/evm/get-evm-conversions-ss58-to-address) | `GET` | `/v1/evm/conversions/ss58-to-address` | | [Get EVM Logs](https://taostats.io/docs/new/evm/get-evm-logs) | `GET` | `/v1/evm/logs` | | [Get EVM Transactions](https://taostats.io/docs/new/evm/get-evm-transactions) | `GET` | `/v1/evm/transactions` | --- # Get EVM Address From SS58 Look up the EVM address that mirrors a given SS58 account. _Source: https://taostats.io/docs/new/evm/get-evm-address-from-ss58_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/address_from_ss58 ``` Requires an API key in the `Authorization` header. Look up the EVM address that mirrors a given SS58 account. Returns a plain JSON string (the EVM address); `404` when no mirror has been observed for the account. Matches the legacy `/api/evm/address_from_ss58/v1` contract. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/address_from_ss58" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/address_from_ss58', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/address_from_ss58", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `ss58_address` | query | `string` | Yes | SS58 address to look up. Required. | ## Responses ### `200` — EVM address as a plain JSON string (0x-prefixed, lowercase) Returns `text/plain` — a `string` body. ### `400` — Missing ss58_address | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — No EVM mirror found for the SS58 account | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get EVM Blocks Paginated EVM blocks. _Source: https://taostats.io/docs/new/evm/get-evm-blocks_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/blocks ``` Requires an API key in the `Authorization` header. Paginated EVM blocks. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/blocks" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/blocks', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/blocks", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block-number range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block-number range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Clamped into [1, 200] (out-of-range values are clamped, not rejected, matching the old API). | | `order_by` | query | `string` | | Column to order by: `block_number` (default) or `timestamp`. | | `order_dir` | query | `string` | | Sort direction: `asc` or `desc`. Default: `desc`. | ## Responses ### `200` — Paginated list of EVM blocks | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].base_fee_per_gas` | `string` | Yes | Base fee per gas (decimal string). | | `data[].difficulty` | `string` | Yes | Block difficulty (decimal string). | | `data[].extra_data` | `string` | Yes | Extra data. | | `data[].gas_limit` | `string` | Yes | Gas limit (decimal string). | | `data[].gas_used` | `string` | Yes | Gas used (decimal string). | | `data[].hash` | `string` | Yes | Block hash. | | `data[].miner` | `string` | Yes | Miner address. | | `data[].nonce` | `string, nullable` | Yes | Block nonce. | | `data[].number` | `integer (int32)` | Yes | EVM block number. | | `data[].parent_hash` | `string` | Yes | Parent block hash. | | `data[].sha_3_uncles` | `string` | Yes | Uncles (sha3) hash. | | `data[].size` | `integer (int32)` | Yes | Block size in bytes. | | `data[].state_root` | `string` | Yes | State root hash. | | `data[].timestamp` | `string` | Yes | Block time, ISO 8601 seconds precision (e.g. `2026-06-27T17:06:00Z`), matching the old API exactly. | | `data[].total_difficulty` | `string` | Yes | Total chain difficulty (decimal string). | | `data[].transaction_root` | `string, nullable` | Yes | Transaction trie root. | | `data[].uncles` | `string, nullable` | Yes | Uncles data. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get EVM Contracts Paginated deployed EVM contracts. _Source: https://taostats.io/docs/new/evm/get-evm-contracts_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/contracts ``` Requires an API key in the `Authorization` header. Paginated deployed EVM contracts. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/contracts" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/contracts', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/contracts", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `address` | query | `string` | | Filter by exact contract address (lower-cased server-side). | | `block_start` | query | `integer (int32)` | | Block-number range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block-number range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Out-of-range values are clamped into `[1, 200]` (e.g. `0`→1, `9999`→200), matching the old API rather than 400ing. | | `order_by` | query | `string` | | Column to order by: `timestamp` (the only allowed value, default). | | `order_dir` | query | `string` | | Sort direction: `asc` or `desc`. Default: `desc`. | ## Responses ### `200` — Paginated list of EVM contracts | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].abi` | `object` | Yes | Contract ABI as JSON, if known (always `null` — never populated). | | `data[].address` | `string` | Yes | Contract address (lower-case hex). | | `data[].block_number` | `integer (int32)` | Yes | Block the contract was deployed in. | | `data[].created_by` | `string` | Yes | Deployer address (lower-case hex) — the `from` of the creation tx. | | `data[].decimals` | `integer (int32), nullable` | Yes | Token decimals, if available. | | `data[].erc1155` | `boolean` | Yes | Detected as an ERC-1155 (multi-token) contract. | | `data[].erc165` | `boolean` | Yes | Implements the ERC-165 interface-detection standard. | | `data[].erc20` | `boolean` | Yes | Detected as an ERC-20 token. | | `data[].erc721` | `boolean` | Yes | Detected as an ERC-721 (NFT) token. | | `data[].input` | `string` | Yes | Creation bytecode (the input the type flags are derived from). | | `data[].name` | `string, nullable` | Yes | Token name, if available. | | `data[].owner` | `string, nullable` | Yes | Owner address (EIP-55 checksummed), if the contract exposes one. | | `data[].symbol` | `string, nullable` | Yes | Token symbol, if the contract is an ERC-20 with readable metadata. | | `data[].timestamp` | `string` | Yes | Deployment time, ISO 8601 seconds precision (e.g. `2026-06-27T17:06:00Z`). | | `data[].transaction_hash` | `string` | Yes | Creation transaction hash. | | `data[].uniswap_v2` | `boolean` | Yes | Detected as a Uniswap V2 pair/contract. | | `data[].uniswap_v3` | `boolean` | Yes | Detected as a Uniswap V3 pool/contract. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get EVM Conversions SS58 To Address Pure SS58 → EVM address conversion. _Source: https://taostats.io/docs/new/evm/get-evm-conversions-ss58-to-address_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/conversions/ss58-to-address ``` Requires an API key in the `Authorization` header. Pure SS58 → EVM address conversion. Returns a plain JSON string (not the standard envelope), matching the legacy contract in `docs/api_spec.md`. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/conversions/ss58-to-address" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/conversions/ss58-to-address', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/conversions/ss58-to-address", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `ss58_address` | query | `string` | Yes | SS58 address to convert. Required. | ## Responses ### `200` — EVM address as a plain JSON string (0x-prefixed, lowercase) Returns `text/plain` — a `string` body. ### `400` — Missing or invalid ss58_address | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get EVM Logs Paginated decoded EVM logs. _Source: https://taostats.io/docs/new/evm/get-evm-logs_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/logs ``` Requires an API key in the `Authorization` header. Paginated decoded EVM logs. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/logs" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/logs', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/logs", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block-number range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block-number range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `transaction_hash` | query | `string` | | Filter by emitting transaction hash (exact). | | `address` | query | `string` | | Filter by emitting contract address (lowercased server-side). | | `event_name` | query | `string` | | Filter by decoded event name (exact). | | `topic0` | query | `string` | | Filter by topic 0 (event signature hash, exact). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Clamped into [1, 200] (out-of-range values are clamped, not rejected, matching the old API). | | `order_by` | query | `string` | | Column to order by: `id` (default), `block_number`, or `timestamp`. | | `order_dir` | query | `string` | | Sort direction: `asc` or `desc`. Default: `desc`. | ## Responses ### `200` — Paginated list of decoded EVM logs | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].abi_json` | `object` | Yes | Decoded event ABI fragment (parsed JSON), or `null`. | | `data[].address` | `string` | Yes | Emitting contract address. | | `data[].args` | `object` | Yes | Decoded event arguments (parsed JSON), or `null` when not decoded. | | `data[].block_number` | `integer (int32)` | Yes | Block number the log was emitted in. | | `data[].data` | `string, nullable` | Yes | Non-indexed log data (hex). | | `data[].event_name` | `string, nullable` | Yes | Decoded event name, or `unknown` when the signature is not recognised. | | `data[].full_signature` | `string, nullable` | Yes | Full event signature, or `null`. | | `data[].hashable_signature` | `string, nullable` | Yes | Hashable event signature, or `null`. | | `data[].id` | `string` | Yes | Log id, `{block_number}.{transaction_index}.{log_index}`. | | `data[].index` | `integer (int32)` | Yes | Log index within the block. | | `data[].removed` | `boolean` | Yes | Whether the log was removed by a chain reorg. | | `data[].timestamp` | `string` | Yes | Block time, ISO 8601 seconds precision (e.g. `2026-06-27T17:06:00Z`). | | `data[].topic0` | `string, nullable` | Yes | Indexed topic 0 (event signature hash). | | `data[].topic1` | `string, nullable` | Yes | Indexed topic 1. | | `data[].topic2` | `string, nullable` | Yes | Indexed topic 2. | | `data[].topic3` | `string, nullable` | Yes | Indexed topic 3. | | `data[].topic4` | `string, nullable` | Yes | Indexed topic 4. | | `data[].transaction_hash` | `string` | Yes | Hash of the transaction that emitted the log. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get EVM Transactions Paginated decoded EVM transactions. _Source: https://taostats.io/docs/new/evm/get-evm-transactions_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/evm/transactions ``` Requires an API key in the `Authorization` header. Paginated decoded EVM transactions. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/evm/transactions" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/evm/transactions', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/evm/transactions", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block-number range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block-number range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `hash` | query | `string` | | Filter by exact transaction hash. | | `address` | query | `string` | | Filter by `from` OR `to` address (lower-cased server-side). | | `to` | query | `string` | | Filter by recipient (`to`) address (lower-cased server-side). | | `from` | query | `string` | | Filter by sender (`from`) address (lower-cased server-side). | | `method_name` | query | `string` | | Filter by decoded method name. | | `method_id` | query | `string` | | Filter by 4-byte method id, bare lower-case hex with NO `0x` prefix (e.g. `a9059cbb`) — matched verbatim against the stored column, exactly as the old API does. | | `contract_created` | query | `string` | | Filter by created-contract address (lower-cased server-side). | | `index` | query | `integer (int32)` | | Filter by transaction index within the block. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Out-of-range values are clamped into `[1, 200]` (e.g. `0`→1, `9999`→200), matching the old API rather than 400ing. | | `order_by` | query | `string` | | Column to order by: `block_number` (default) or `timestamp`. | | `order_dir` | query | `string` | | Sort direction: `asc` or `desc`. Default: `desc`. | ## Responses ### `200` — Paginated list of decoded EVM transactions | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].abi_json` | `object` | Yes | Decoded ABI fragment as JSON, or `null` when the selector is unknown. | | `data[].args` | `object` | Yes | Decoded call arguments as a JSON object, or `null` when undecodable. | | `data[].block_hash` | `string` | Yes | Block hash. | | `data[].block_number` | `integer (int32)` | Yes | Block number containing the transaction. | | `data[].contract_created` | `string, nullable` | Yes | Address of the contract created by this transaction, if any. | | `data[].cumulative_gas_used` | `string` | Yes | Cumulative gas used in the block up to and including this tx. | | `data[].effective_gas_price` | `string` | Yes | Effective gas price (decimal string). | | `data[].from` | `string` | Yes | Sender address (`from_address`). | | `data[].full_signature` | `string, nullable` | Yes | Full function signature (e.g. `transfer(address,uint256)`), or `null`. | | `data[].gas` | `string` | Yes | Gas limit (decimal string). | | `data[].gas_price` | `string` | Yes | Gas price (decimal string). | | `data[].gas_used` | `string` | Yes | Gas used by this transaction (decimal string). | | `data[].hash` | `string` | Yes | Transaction hash. | | `data[].hashable_signature` | `string, nullable` | Yes | Hashable function signature, or `null`. | | `data[].index` | `integer (int32)` | Yes | Transaction index within the block. | | `data[].input` | `string` | Yes | Raw call input data (`0x…`). | | `data[].max_fee_per_gas` | `string, nullable` | Yes | EIP-1559 max fee per gas (decimal string), or `null`. | | `data[].max_priority_fee_per_gas` | `string, nullable` | Yes | EIP-1559 max priority fee per gas (decimal string), or `null`. | | `data[].method_id` | `string, nullable` | Yes | 4-byte method selector as bare lower-case hex (8 chars, NO `0x` prefix — e.g. `a9059cbb`), matching the old API and the stored column (`input[2..10]`). `null` for native transfers (input shorter than 10). | | `data[].method_name` | `string, nullable` | Yes | Decoded method name (e.g. `transfer`, `native_transfer`, `unknown`). | | `data[].nonce` | `integer (int32)` | Yes | Transaction nonce. | | `data[].success` | `boolean` | Yes | Whether the transaction succeeded. | | `data[].timestamp` | `string` | Yes | Block time, ISO 8601 seconds precision (e.g. `2026-06-27T17:06:00Z`). | | `data[].to` | `string, nullable` | Yes | Recipient address (`to_address`), or `null` for contract creation. | | `data[].value` | `string` | Yes | Transaction value in wei (decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Historic Every Taostats API endpoint in the Historic group, with its method and path. _Source: https://taostats.io/docs/new/historic_ _Last reviewed: 2026-10-07_ One Historic endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Get Historic Stake Events](https://taostats.io/docs/new/historic/get-historic-stake-events) | `GET` | `/v1/historic/stake-events` | --- # Get Historic Stake Events Paginated list of pre-dTAO raw TAO stake/unstake events. _Source: https://taostats.io/docs/new/historic/get-historic-stake-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/historic/stake-events ``` Requires an API key in the `Authorization` header. Paginated list of pre-dTAO raw TAO stake/unstake events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/historic/stake-events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/historic/stake-events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/historic/stake-events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Coldkey address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `hotkey` | query | `string` | | Hotkey address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `action` | query | `string` | | Action filter. One of `stake`, `unstake`, `all`. `all` skips the action filter entirely. Default: `all`. One of `stake`, `unstake`, `all`. | | `extrinsic_id` | query | `string` | | Exact extrinsic ID (e.g. `5000000-0002`). | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of historic stake events matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].action` | `string` | Yes | `stake` or `unstake`. | | `data[].amount` | `string` | Yes | TAO amount (RAO) as a decimal string (u64). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | Coldkey SS58 address. | | `data[].extrinsic_id` | `string, nullable` | Yes | Parent extrinsic ID. `null` when the indexer didn't record one. | | `data[].hotkey` | `string` | Yes | Hotkey SS58 address. | | `data[].id` | `string` | Yes | Event ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Miners Every Taostats API endpoint in the Miners group, with its method and path. _Source: https://taostats.io/docs/new/miners_ _Last reviewed: 2026-10-07_ 4 Miners endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Miners Autostakes](https://taostats.io/docs/new/miners/get-miners-autostakes) | `GET` | `/v1/miners/autostakes` | | [Get Miners Coldkey Summary](https://taostats.io/docs/new/miners/get-miners-coldkey-summary) | `GET` | `/v1/miners/coldkey-summary` | | [Get Miners Weights History](https://taostats.io/docs/new/miners/get-miners-weights-history) | `GET` | `/v1/miners/weights/history` | | [Get Miners Weights](https://taostats.io/docs/new/miners/get-miners-weights) | `GET` | `/v1/miners/weights` | --- # Get Miners Autostakes Paginated miner autostake event history. _Source: https://taostats.io/docs/new/miners/get-miners-autostakes_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/miners/autostakes ``` Requires an API key in the `Authorization` header. Paginated miner autostake event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/miners/autostakes" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/miners/autostakes', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/miners/autostakes", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `hotkey` | query | `string` | | Miner hotkey (SS58 or 0x-hex; normalized to SS58). Exact match. | | `coldkey` | query | `string` | | Coldkey (SS58 or 0x-hex; normalized to SS58). Exact match. | | `destination_hotkey` | query | `string` | | Destination hotkey (SS58 or 0x-hex; normalized to SS58). Exact match. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp` is supported. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of miner autostake events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Alpha auto-staked (RAO), as a decimal string. | | `data[].block_number` | `integer (int32)` | Yes | Block height the event was emitted in. | | `data[].coldkey` | `string` | Yes | Coldkey owning the miner hotkey, SS58 address. | | `data[].destination_hotkey` | `string` | Yes | Hotkey the incentive was auto-staked onto, SS58 address. | | `data[].hotkey` | `string` | Yes | Miner hotkey whose incentive was auto-staked, SS58 address. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Miners Coldkey Summary GET /v1/miners/coldkey-summary — Taostats API endpoint in the Miners group. _Source: https://taostats.io/docs/new/miners/get-miners-coldkey-summary_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/miners/coldkey-summary ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/miners/coldkey-summary" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/miners/coldkey-summary', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/miners/coldkey-summary", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | Yes | Coldkey, SS58 or 0x-prefixed hex. | | `days` | query | `integer (int32)` | Yes | How many days of history the windowed totals cover. Required, matching the OLD API. Between 1 and 36,500. | ## Responses ### `200` — Mining summary for one coldkey | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | The summary, as a one-element array: `data` is always an array (`docs/api_standards.md`). | | `data[].active_subnets` | `integer (int32)` | Yes | Distinct subnets the coldkey currently mines in. | | `data[].alpha_balances` | `array` | Yes | Every alpha position the coldkey holds, mining or not. | | `data[].alpha_balances[].balance` | `string` | Yes | Alpha balance (RAO, `u64` decimal string). | | `data[].alpha_balances[].balance_as_tao` | `string` | Yes | The same balance valued in TAO (RAO, `u64` decimal string). | | `data[].alpha_balances[].coldkey` | `string` | Yes | Coldkey that owns the position (SS58). | | `data[].alpha_balances[].hotkey` | `string` | Yes | Hotkey the alpha is staked to (SS58). | | `data[].alpha_balances[].netuid` | `integer (int32)` | Yes | Subnet the alpha belongs to. | | `data[].average_mining_emission_as_tao_per_hotkey` | `string` | Yes | That total divided by `total_active_hotkeys`, floored (RAO, decimal string). `"0"` when there are no active hotkeys. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].free_balance` | `string` | Yes | Free TAO balance (RAO, decimal string). | | `data[].hotkeys` | `array` | Yes | Per-hotkey breakdown: currently registered neurons first, then deregistrations from the window. | | `data[].hotkeys[].alpha_balance` | `string` | Yes | Alpha staked to this hotkey in this subnet (RAO, decimal string). `"0"` when the coldkey holds no position there. | | `data[].hotkeys[].alpha_balance_as_tao` | `string` | Yes | That alpha valued in TAO (RAO, decimal string). | | `data[].hotkeys[].axon` | `string` | Yes | `ip:port` of the neuron's axon, or `""` when it serves none. | | `data[].hotkeys[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].hotkeys[].consensus` | `string` | Yes | Consensus (normalised `u16 / 65535`). | | `data[].hotkeys[].deregistered` | `boolean` | Yes | Whether this row is a deregistration that happened during the window. | | `data[].hotkeys[].deregistration_timestamp` | `string, nullable` | Yes | When it was deregistered (ISO 8601, millisecond precision), or `null` for a currently registered hotkey. | | `data[].hotkeys[].emission` | `string` | Yes | The neuron's emission at the current snapshot (RAO, decimal string). | | `data[].hotkeys[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].hotkeys[].immune` | `boolean` | Yes | Within the subnet's immunity period, so it cannot be deregistered yet. | | `data[].hotkeys[].in_danger` | `boolean` | Yes | Among the subnet's lowest-incentive non-immune neurons, so it is at risk of deregistration. Always `false` for a deregistered hotkey. | | `data[].hotkeys[].incentive` | `string` | Yes | Incentive summed across the subnet's mechanisms, weighted by its emission split (normalised over `65535²`), as the OLD API serves it. On a single-mechanism subnet this is the plain `u16 / 65535` incentive. | | `data[].hotkeys[].mech_incentive` | `array` | Yes | Per-mechanism incentive (normalised `u16 / 65535`, one per mechanism). | | `data[].hotkeys[].mech_incentive[]` | `string` | Yes | | | `data[].hotkeys[].miner_rank` | `integer (int32), nullable` | Yes | Competition rank by incentive within the subnet, `null` when incentive is zero. | | `data[].hotkeys[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].hotkeys[].registration_block` | `integer (int32)` | Yes | Block the neuron registered at. `0` for a deregistered hotkey, which is what the OLD API reports: `neuron_deregistration_v1` does not record a registration block. | | `data[].hotkeys[].total_emission` | `string` | Yes | Alpha emitted to this hotkey across the whole window (RAO, decimal string) — the sum of its per-epoch emission. | | `data[].hotkeys[].total_emission_as_tao` | `string` | Yes | `total_emission` valued in TAO at the current alpha price (RAO, decimal string). | | `data[].hotkeys[].trust` | `string` | Yes | Trust (normalised `u16 / 65535`). | | `data[].hotkeys[].uid` | `integer (int32)` | Yes | Neuron UID within the subnet. | | `data[].hotkeys[].validator_rank` | `integer (int32), nullable` | Yes | Competition rank by dividends within the subnet, `null` when dividends are zero. | | `data[].total_active_hotkeys` | `integer (int32)` | Yes | Currently registered `(subnet, hotkey)` neurons. | | `data[].total_balance` | `string` | Yes | Free plus reserved plus staked, all in RAO (decimal string). | | `data[].total_deregistered_hotkeys` | `integer (int32)` | Yes | Deregistrations recorded for this coldkey during the window. | | `data[].total_hotkeys_in_danger` | `integer (int32)` | Yes | How many of those are in danger. | | `data[].total_hotkeys_in_danger_during_period` | `integer (int32)` | Yes | Distinct `(subnet, hotkey)` neurons that were in danger at any epoch in the window, including ones since deregistered. | | `data[].total_immune_hotkeys` | `integer (int32)` | Yes | How many of those are immune. | | `data[].total_immune_hotkeys_during_period` | `integer (int32)` | Yes | Distinct `(subnet, hotkey)` neurons that were immune at any epoch in the window, including ones since deregistered. | | `data[].total_mining_emission_as_tao` | `string` | Yes | Every epoch's emission across the window, each row valued in TAO at the current alpha price (RAO, decimal string). | | `data[].total_staked_balance_as_tao` | `string` | Yes | Every alpha position valued in TAO (RAO, decimal string). Derived by summing `alpha_balances`, so the response's own numbers add up rather than mixing two snapshots. | | `data[].total_staked_mining_balance_as_tao` | `string` | Yes | The part of that staked in a subnet the coldkey currently mines in, on the mining hotkey (RAO, decimal string). | | `data[].total_staked_non_mining_balance_as_tao` | `string` | Yes | The rest (RAO, decimal string). | | `pagination` | `object` | Yes | Pagination block. Always one item on one page. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Miners Weights History GET /v1/miners/weights/history — Taostats API endpoint in the Miners group. _Source: https://taostats.io/docs/new/miners/get-miners-weights-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/miners/weights/history ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/miners/weights/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/miners/weights/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/miners/weights/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | | `mechanism` | query | `integer (int32)` | | Subnet mechanism (0-15). Default: 0, the main mechanism. | | `miner_uid` | query | `integer (int32)` | | | | `validator_uid` | query | `integer (int32)` | | | | `miner_hotkey` | query | `string` | | | | `validator_hotkey` | query | `string` | | | | `block_number` | query | `integer (int32)` | | | | `block_start` | query | `integer (int32)` | | | | `block_end` | query | `integer (int32)` | | | | `timestamp_start` | query | `integer (int64)` | | | | `timestamp_end` | query | `integer (int64)` | | | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | | | `order_by` | query | `string` | | Order column: `timestamp` (only). | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Miner weight assignments over time for a subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].mechanism` | `integer (int32)` | Yes | Subnet mechanism the weight was set on (0 is the main mechanism). | | `data[].miner_hotkey` | `string` | Yes | Miner hotkey (SS58), or `"unknown"` if the UID has no live neuron. | | `data[].miner_uid` | `integer (int32)` | Yes | Miner (target) neuron UID. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].validator_hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].validator_uid` | `integer (int32)` | Yes | Validator (source) neuron UID. | | `data[].weight` | `string` | Yes | Weight value (sum-normalised fraction, decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Miners Weights GET /v1/miners/weights — Taostats API endpoint in the Miners group. _Source: https://taostats.io/docs/new/miners/get-miners-weights_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/miners/weights ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/miners/weights" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/miners/weights', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/miners/weights", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | | `mechanism` | query | `integer (int32)` | | Subnet mechanism (0-15). Default: 0, the main mechanism. | | `miner_uid` | query | `integer (int32)` | | | | `validator_uid` | query | `integer (int32)` | | | | `miner_hotkey` | query | `string` | | | | `validator_hotkey` | query | `string` | | | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | | | `order_by` | query | `string` | | Order column: `validator_uid` (default), `netuid`, or `miner_uid`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Current miner weight assignments for a subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].mechanism` | `integer (int32)` | Yes | Subnet mechanism the weight was set on (0 is the main mechanism). | | `data[].miner_hotkey` | `string` | Yes | Miner hotkey (SS58), or `"unknown"` if the UID has no live neuron. | | `data[].miner_uid` | `integer (int32)` | Yes | Miner (target) neuron UID. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].validator_hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].validator_uid` | `integer (int32)` | Yes | Validator (source) neuron UID. | | `data[].weight` | `string` | Yes | Weight value (sum-normalised fraction, decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Network Every Taostats API endpoint in the Network group, with its method and path. _Source: https://taostats.io/docs/new/network_ _Last reviewed: 2026-10-07_ 5 Network endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Network Parameters](https://taostats.io/docs/new/network/get-network-parameters) | `GET` | `/v1/network/parameters` | | [Get Network Runtime Version History](https://taostats.io/docs/new/network/get-network-runtime-version-history) | `GET` | `/v1/network/runtime-version/history` | | [Get Network Runtime Version](https://taostats.io/docs/new/network/get-network-runtime-version) | `GET` | `/v1/network/runtime-version` | | [Get Network Stats History](https://taostats.io/docs/new/network/get-network-stats-history) | `GET` | `/v1/network/stats/history` | | [Get Network Stats](https://taostats.io/docs/new/network/get-network-stats) | `GET` | `/v1/network/stats` | --- # Get Network Parameters Latest global network parameters — the newer of the live row and the newest daily row. _Source: https://taostats.io/docs/new/network/get-network-parameters_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/network/parameters ``` Requires an API key in the `Authorization` header. Latest global network parameters — the newer of the live row and the newest daily row. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/network/parameters" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/network/parameters', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/network/parameters", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest global network parameters | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `object` | Yes | Latest global network parameters, shaped for HTTP JSON output. All fields are nullable per `docs/api_spec.md`; the `ValueQuery` storage items are always populated, while the three documented fields can be `null`. | | `data.block_number` | `integer (int32)` | Yes | Block height at which this snapshot was taken. | | `data.coldkey_swap_announcement_delay` | `integer (int32), nullable` | Yes | Coldkey swap announcement delay (blocks). | | `data.coldkey_swap_reannouncement_delay` | `integer (int32), nullable` | Yes | Coldkey swap reannouncement delay (blocks). | | `data.coldkey_swap_schedule_duration` | `integer (int32), nullable` | Yes | Coldkey swap schedule duration (blocks). Null on current runtimes — the backing storage was removed by a chain migration. | | `data.dissolve_network_schedule_duration` | `integer (int32), nullable` | Yes | Network dissolve schedule duration (blocks). | | `data.max_childkey_take` | `string, nullable` | Yes | Max childkey take (decimal, 0-1). | | `data.max_delegate_take` | `string, nullable` | Yes | Max delegate take (decimal, 0-1). | | `data.min_childkey_take` | `string, nullable` | Yes | Min childkey take (decimal, 0-1). | | `data.min_delegate_take` | `string, nullable` | Yes | Min delegate take (decimal, 0-1). | | `data.network_immunity_period` | `string, nullable` | Yes | Network immunity period (u64 as string). | | `data.network_last_registered` | `string, nullable` | Yes | Last registered network block (u64 as string). Null only on the pre-migration standalone-storage window. | | `data.network_lock_reduction_interval` | `string, nullable` | Yes | Lock reduction interval (u64 as string). | | `data.network_min_lock_cost` | `string, nullable` | Yes | Min lock cost (RAO, u64 as string). | | `data.network_rate_limit` | `string, nullable` | Yes | Network rate limit (u64 as string). | | `data.nominator_min_required_stake` | `string, nullable` | Yes | Min nominator stake (RAO, u64 as string). | | `data.pending_childkey_cooldown` | `integer (int64), nullable` | Yes | Childkey cooldown (blocks). | | `data.senate_required_stake_percentage` | `string, nullable` | Yes | Senate required stake percentage (decimal, 0-1). Always null — the senate was removed from the chain (only an orphaned storage value remains) and the old-API contract is null. | | `data.stake_threshold` | `string, nullable` | Yes | Stake threshold (RAO, u64 as string). | | `data.subnet_moving_alpha` | `string, nullable` | Yes | Subnet moving alpha (raw I96F32 bits, as string). | | `data.subnet_owner_cut` | `string, nullable` | Yes | Subnet owner cut (decimal, 0-1). | | `data.timestamp` | `string` | Yes | ISO 8601 / RFC 3339 timestamp of the snapshot block. | | `data.tx_childkey_take_rate_limit` | `string, nullable` | Yes | Childkey take rate limit (u64 as string). | | `data.tx_delegate_take_rate_limit` | `string, nullable` | Yes | Delegate take rate limit (u64 as string). | | `data.tx_rate_limit` | `string, nullable` | Yes | Transaction rate limit (u64 as string). | ### `404` — No network parameters snapshot recorded yet | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Network Runtime Version History GET /v1/network/runtime-version/history — Taostats API endpoint in the Network group. _Source: https://taostats.io/docs/new/network/get-network-runtime-version-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/network/runtime-version/history ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/network/runtime-version/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/network/runtime-version/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/network/runtime-version/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated runtime version history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height at which the runtime version changed. | | `data[].runtime_version` | `integer (int32)` | Yes | Runtime spec version active from this block onwards. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Network Runtime Version Current runtime version snapshot — the latest row by block height. _Source: https://taostats.io/docs/new/network/get-network-runtime-version_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/network/runtime-version ``` Requires an API key in the `Authorization` header. Current runtime version snapshot — the latest row by block height. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/network/runtime-version" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/network/runtime-version', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/network/runtime-version", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest runtime version observed on the indexed network | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `object` | Yes | One row of the runtime-version response, shaped for HTTP JSON output. `timestamp` is rendered with explicit millisecond precision (`.000Z`) to match the sibling network endpoints (issue #420), distinct from the ClickHouse row type which uses millis-since-epoch. | | `data.block_number` | `integer (int32)` | Yes | Block height at which the runtime version changed. | | `data.runtime_version` | `integer (int32)` | Yes | Runtime spec version active from this block onwards. | | `data.timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | ### `404` — No runtime version recorded for the configured network yet | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Network Stats History GET /v1/network/stats/history — Taostats API endpoint in the Network group. _Source: https://taostats.io/docs/new/network/get-network-stats-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/network/stats/history ``` Requires an API key in the `Authorization` header. Daily time series. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/network/stats/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/network/stats/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/network/stats/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Network-wide statistics over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].accounts` | `integer (int64)` | Yes | Total accounts. | | `data[].balance_holders` | `integer (int64)` | Yes | Accounts with balance. | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].extrinsics` | `integer (int64)` | Yes | Total extrinsics. | | `data[].free` | `string` | Yes | Circulating supply (RAO, u64 as string). | | `data[].issued` | `string` | Yes | Total issued (RAO, u64 as string). | | `data[].staked` | `string` | Yes | Total staked (RAO, u64 as string). | | `data[].staked_alpha` | `string, nullable` | | Total staked alpha (RAO, u64 as string). `null` pre-dTAO. | | `data[].staked_root` | `string, nullable` | | Root stake the stakers hold, `TotalHotkeyAlpha(_, 0)` summed (RAO, u64 as string) — not the root pool's reserve, so it need not add up with `staked_alpha` to `staked` (#827). `null` pre-dTAO. | | `data[].subnet_locked` | `string` | Yes | Total subnet locked (RAO, u64 as string). | | `data[].subnets` | `integer (int64)` | Yes | Total subnets. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].transfers` | `integer (int64)` | Yes | Total transfers. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Network Stats GET /v1/network/stats — Taostats API endpoint in the Network group. _Source: https://taostats.io/docs/new/network/get-network-stats_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/network/stats ``` Requires an API key in the `Authorization` header. Latest snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/network/stats" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/network/stats', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/network/stats", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest network-wide statistics | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `object` | Yes | One row of network-wide statistics. | | `data.accounts` | `integer (int64)` | Yes | Total accounts. | | `data.balance_holders` | `integer (int64)` | Yes | Accounts with balance. | | `data.block_number` | `integer (int32)` | Yes | Block height. | | `data.extrinsics` | `integer (int64)` | Yes | Total extrinsics. | | `data.free` | `string` | Yes | Circulating supply (RAO, u64 as string). | | `data.issued` | `string` | Yes | Total issued (RAO, u64 as string). | | `data.staked` | `string` | Yes | Total staked (RAO, u64 as string). | | `data.staked_alpha` | `string, nullable` | | Total staked alpha (RAO, u64 as string). `null` pre-dTAO. | | `data.staked_root` | `string, nullable` | | Root stake the stakers hold, `TotalHotkeyAlpha(_, 0)` summed (RAO, u64 as string) — not the root pool's reserve, so it need not add up with `staked_alpha` to `staked` (#827). `null` pre-dTAO. | | `data.subnet_locked` | `string` | Yes | Total subnet locked (RAO, u64 as string). | | `data.subnets` | `integer (int64)` | Yes | Total subnets. | | `data.timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data.transfers` | `integer (int64)` | Yes | Total transfers. | ### `404` — No data yet | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # OTC Every Taostats API endpoint in the OTC group, with its method and path. _Source: https://taostats.io/docs/new/otc_ _Last reviewed: 2026-10-07_ 19 OTC endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get OTC Contract V1 Listings History](https://taostats.io/docs/new/otc/get-otc-contract-v1-listings-history) | `GET` | `/v1/otc/contract-v1/listings/history` | | [Get OTC Contract V1 Listings](https://taostats.io/docs/new/otc/get-otc-contract-v1-listings) | `GET` | `/v1/otc/contract-v1/listings` | | [Get OTC Contract V1 Offers History](https://taostats.io/docs/new/otc/get-otc-contract-v1-offers-history) | `GET` | `/v1/otc/contract-v1/offers/history` | | [Get OTC Contract V1 Offers](https://taostats.io/docs/new/otc/get-otc-contract-v1-offers) | `GET` | `/v1/otc/contract-v1/offers` | | [Get OTC Contract V1 Subnets Status](https://taostats.io/docs/new/otc/get-otc-contract-v1-subnets-status) | `GET` | `/v1/otc/contract-v1/subnets/status` | | [Get OTC Contract V1 Trades](https://taostats.io/docs/new/otc/get-otc-contract-v1-trades) | `GET` | `/v1/otc/contract-v1/trades` | | [Get OTC Contract V1 Users Stats](https://taostats.io/docs/new/otc/get-otc-contract-v1-users-stats) | `GET` | `/v1/otc/contract-v1/users/stats` | | [Get OTC Listings History](https://taostats.io/docs/new/otc/get-otc-listings-history) | `GET` | `/v1/otc/listings/history` | | [Get OTC Listings](https://taostats.io/docs/new/otc/get-otc-listings) | `GET` | `/v1/otc/listings` | | [Get OTC Lockup Claims](https://taostats.io/docs/new/otc/get-otc-lockup-claims) | `GET` | `/v1/otc/lockup/claims` | | [Get OTC Lockup Listings History](https://taostats.io/docs/new/otc/get-otc-lockup-listings-history) | `GET` | `/v1/otc/lockup/listings/history` | | [Get OTC Lockup Listings](https://taostats.io/docs/new/otc/get-otc-lockup-listings) | `GET` | `/v1/otc/lockup/listings` | | [Get OTC Lockup Purchases](https://taostats.io/docs/new/otc/get-otc-lockup-purchases) | `GET` | `/v1/otc/lockup/purchases` | | [Get OTC Lockup Users Stats](https://taostats.io/docs/new/otc/get-otc-lockup-users-stats) | `GET` | `/v1/otc/lockup/users/stats` | | [Get OTC Offers History](https://taostats.io/docs/new/otc/get-otc-offers-history) | `GET` | `/v1/otc/offers/history` | | [Get OTC Offers](https://taostats.io/docs/new/otc/get-otc-offers) | `GET` | `/v1/otc/offers` | | [Get OTC Subnets Status](https://taostats.io/docs/new/otc/get-otc-subnets-status) | `GET` | `/v1/otc/subnets/status` | | [Get OTC Trades](https://taostats.io/docs/new/otc/get-otc-trades) | `GET` | `/v1/otc/trades` | | [Get OTC Users Stats](https://taostats.io/docs/new/otc/get-otc-users-stats) | `GET` | `/v1/otc/users/stats` | --- # Get OTC Contract V1 Listings History Paginated alpha-listing event history. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-listings-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/listings/history ``` Requires an API key in the `Authorization` header. Paginated alpha-listing event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/listings/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/listings/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/listings/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `event_type` | query | `string` | | `created`, `cancelled`, `taken`, or `all` (default). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated alpha-listing event history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | | | `data[].amount_returned` | `string, nullable` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string, nullable` | Yes | | | `data[].event_type` | `string` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string, nullable` | Yes | | | `data[].force_cancelled` | `boolean, nullable` | Yes | | | `data[].hotkey` | `string` | Yes | | | `data[].id` | `string` | Yes | | | `data[].initiated_by` | `string, nullable` | Yes | | | `data[].listing_id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].price` | `string` | Yes | Absolute price (RAO of TAO per whole alpha), as a decimal string. A `cancelled` row carries `"0"`, exactly as the OLD API serves it. | | `data[].seller` | `string` | Yes | | | `data[].tao_amount` | `string, nullable` | Yes | | | `data[].timestamp` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Listings Paginated current OTC alpha listings. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-listings_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/listings ``` Requires an API key in the `Authorization` header. Paginated current OTC alpha listings. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/listings" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/listings', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/listings", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `active`, `cancelled`, `taken`, or `all` (default). | | `price_min` | query | `string` | | Minimum price (RAO, inclusive). | | `price_max` | query | `string` | | Maximum price (RAO, inclusive). | | `amount_min` | query | `string` | | Minimum amount (RAO, inclusive). | | `amount_max` | query | `string` | | Maximum amount (RAO, inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of OTC alpha listings | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Alpha amount (RAO), as a decimal string. | | `data[].created_block` | `integer (int32)` | Yes | Block the listing was created in. | | `data[].created_timestamp` | `string` | Yes | Creation time, ISO 8601 with millisecond precision. | | `data[].force_cancelled` | `boolean` | Yes | Whether the listing was force-cancelled. | | `data[].hotkey` | `string` | Yes | Hotkey the alpha is staked to, SS58 address. | | `data[].initiated_by` | `string, nullable` | | Account that initiated a cancellation, SS58 address (null otherwise). | | `data[].listing_id` | `string` | Yes | Listing ID. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price` | `string` | Yes | Absolute price (RAO of TAO per whole alpha), as a decimal string. | | `data[].seller` | `string` | Yes | Seller coldkey, SS58 address. | | `data[].status` | `string` | Yes | `active`, `cancelled`, or `taken`. | | `data[].updated_block` | `integer (int32)` | Yes | Block of the last update. | | `data[].updated_timestamp` | `string` | Yes | Last-update time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Offers History Paginated TAO-offer event history. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-offers-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/offers/history ``` Requires an API key in the `Authorization` header. Paginated TAO-offer event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/offers/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/offers/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/offers/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `event_type` | query | `string` | | `created`, `cancelled`, `taken`, or `all` (default). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated TAO-offer event history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string, nullable` | Yes | | | `data[].amount` | `string` | Yes | | | `data[].amount_returned` | `string, nullable` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string` | Yes | | | `data[].event_type` | `string` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string, nullable` | Yes | | | `data[].id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].offer_id` | `string` | Yes | | | `data[].price` | `string` | Yes | Absolute price (RAO of TAO per whole alpha), as a decimal string. A `cancelled` row carries `"0"`, exactly as the OLD API serves it. | | `data[].seller` | `string, nullable` | Yes | | | `data[].timestamp` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Offers Paginated current OTC TAO offers. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-offers_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/offers ``` Requires an API key in the `Authorization` header. Paginated current OTC TAO offers. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/offers" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/offers', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/offers", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `active`, `cancelled`, `taken`, or `all` (default). | | `price_min` | query | `string` | | Minimum price (RAO, inclusive). | | `price_max` | query | `string` | | Maximum price (RAO, inclusive). | | `amount_min` | query | `string` | | Minimum amount (RAO, inclusive). | | `amount_max` | query | `string` | | Maximum amount (RAO, inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of OTC TAO offers | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | TAO amount (RAO), as a decimal string. | | `data[].buyer` | `string` | Yes | Buyer coldkey, SS58 address. | | `data[].created_block` | `integer (int32)` | Yes | Block the offer was created in. | | `data[].created_timestamp` | `string` | Yes | Creation time, ISO 8601 with millisecond precision. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].offer_id` | `string` | Yes | Offer ID. | | `data[].price` | `string` | Yes | Absolute price (RAO of TAO per whole alpha), as a decimal string. | | `data[].status` | `string` | Yes | `active`, `cancelled`, or `taken`. | | `data[].updated_block` | `integer (int32)` | Yes | Block of the last update. | | `data[].updated_timestamp` | `string` | Yes | Last-update time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Subnets Status Paginated current OTC status per subnet. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-subnets-status_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/subnets/status ``` Requires an API key in the `Authorization` header. Paginated current OTC status per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/subnets/status" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/subnets/status', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/subnets/status", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `frozen` | query | `string` | | `frozen`, `unfrozen`, or `all` (default). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated OTC subnet status | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block the status change landed in. | | `data[].changed_by` | `string` | Yes | Account that changed the status, SS58 address. | | `data[].frozen` | `boolean` | Yes | Whether OTC trading is frozen. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].reason` | `string` | Yes | Reason for the freeze/unfreeze. | | `data[].timestamp` | `string` | Yes | Change time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Trades Paginated completed OTC trades. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-trades_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/trades ``` Requires an API key in the `Authorization` header. Paginated completed OTC trades. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/trades" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/trades', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/trades", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `trade_type` | query | `string` | | `listing_taken`, `offer_taken`, or `all` (default). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated completed OTC trades | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string` | Yes | | | `data[].id` | `string` | Yes | | | `data[].listing_id` | `string, nullable` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].offer_id` | `string, nullable` | Yes | | | `data[].price` | `string` | Yes | | | `data[].seller` | `string` | Yes | | | `data[].tao_amount` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | | | `data[].trade_type` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Contract V1 Users Stats Paginated per-user OTC statistics. _Source: https://taostats.io/docs/new/otc/get-otc-contract-v1-users-stats_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/contract-v1/users/stats ``` Requires an API key in the `Authorization` header. Paginated per-user OTC statistics. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/contract-v1/users/stats" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/contract-v1/users/stats', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/contract-v1/users/stats", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `account` | query | `string` | | Filter by account (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `last_activity_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated per-user OTC statistics | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].account` | `string` | Yes | | | `data[].last_activity_block` | `integer (int32)` | Yes | | | `data[].last_activity_timestamp` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].total_listings_created` | `integer (int64)` | Yes | | | `data[].total_offers_created` | `integer (int64)` | Yes | | | `data[].total_trades_as_buyer` | `integer (int64)` | Yes | | | `data[].total_trades_as_seller` | `integer (int64)` | Yes | | | `data[].total_volume_alpha` | `string` | Yes | | | `data[].total_volume_tao` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Listings History Paginated alpha-listing event history. _Source: https://taostats.io/docs/new/otc/get-otc-listings-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/listings/history ``` Requires an API key in the `Authorization` header. Paginated alpha-listing event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/listings/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/listings/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/listings/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `event_type` | query | `string` | | `created`, `cancelled`, `taken`, or `all` (default). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated alpha-listing event history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | | | `data[].amount_returned` | `string, nullable` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string, nullable` | Yes | | | `data[].event_type` | `string` | Yes | | | `data[].executed_price` | `string, nullable` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string, nullable` | Yes | | | `data[].force_cancelled` | `boolean, nullable` | Yes | | | `data[].hotkey` | `string` | Yes | | | `data[].id` | `string` | Yes | | | `data[].initiated_by` | `string, nullable` | Yes | | | `data[].listing_id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].price_offset_bps` | `integer (int32)` | Yes | | | `data[].seller` | `string` | Yes | | | `data[].tao_amount` | `string, nullable` | Yes | | | `data[].timestamp` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Listings Paginated current OTC alpha listings. _Source: https://taostats.io/docs/new/otc/get-otc-listings_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/listings ``` Requires an API key in the `Authorization` header. Paginated current OTC alpha listings. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/listings" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/listings', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/listings", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `active`, `cancelled`, `taken`, or `all` (default). | | `price_offset_bps_min` | query | `integer (int32)` | | Minimum price offset (basis points, inclusive). | | `price_offset_bps_max` | query | `integer (int32)` | | Maximum price offset (basis points, inclusive). | | `amount_min` | query | `string` | | Minimum amount (RAO, inclusive). | | `amount_max` | query | `string` | | Maximum amount (RAO, inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of OTC alpha listings | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Alpha amount (RAO), as a decimal string. | | `data[].created_block` | `integer (int32)` | Yes | Block the listing was created in. | | `data[].created_timestamp` | `string` | Yes | Creation time, ISO 8601 with millisecond precision. | | `data[].force_cancelled` | `boolean` | Yes | Whether the listing was force-cancelled. | | `data[].hotkey` | `string` | Yes | Hotkey the alpha is staked to, SS58 address. | | `data[].initiated_by` | `string, nullable` | | Account that initiated a cancellation, SS58 address (null otherwise). | | `data[].listing_id` | `string` | Yes | Listing ID. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price_offset_bps` | `integer (int32)` | Yes | Price offset in basis points. | | `data[].seller` | `string` | Yes | Seller coldkey, SS58 address. | | `data[].status` | `string` | Yes | `active`, `cancelled`, or `taken`. | | `data[].updated_block` | `integer (int32)` | Yes | Block of the last update. | | `data[].updated_timestamp` | `string` | Yes | Last-update time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Lockup Claims Paginated alpha-claim events. _Source: https://taostats.io/docs/new/otc/get-otc-lockup-claims_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/lockup/claims ``` Requires an API key in the `Authorization` header. Paginated alpha-claim events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/lockup/claims" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/lockup/claims', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/lockup/claims", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `escrow_account` | query | `string` | | Filter by escrow account (SS58 or 0x-hex; normalized to SS58). | | `unlock_block_start` | query | `integer (int32)` | | Unlock-block range start (inclusive). | | `unlock_block_end` | query | `integer (int32)` | | Unlock-block range end (inclusive). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of lockup alpha claims | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount_claimed` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string` | Yes | | | `data[].escrow_account` | `string` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].timestamp` | `string` | Yes | | | `data[].unlock_block` | `integer (int32)` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Lockup Listings History Paginated lockup listing history. _Source: https://taostats.io/docs/new/otc/get-otc-lockup-listings-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/lockup/listings/history ``` Requires an API key in the `Authorization` header. Paginated lockup listing history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/lockup/listings/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/lockup/listings/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/lockup/listings/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `event_type` | query | `string` | | `created`, `taken`, `cancelled`, `force_cancelled`, `filled`, or `all`. | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated lockup listing history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string, nullable` | | | | `data[].amount` | `string, nullable` | | | | `data[].amount_returned` | `string, nullable` | | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string, nullable` | | | | `data[].escrow_account` | `string, nullable` | | | | `data[].event_type` | `string` | Yes | | | `data[].executed_price` | `string, nullable` | | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string, nullable` | | | | `data[].force_cancelled` | `boolean, nullable` | | | | `data[].hotkey` | `string, nullable` | | | | `data[].id` | `string` | Yes | | | `data[].initiated_by` | `string, nullable` | | | | `data[].listing_id` | `string` | Yes | | | `data[].lockup_duration` | `integer (int32), nullable` | | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].price_offset_bps` | `integer (int32), nullable` | | | | `data[].purchase_id` | `string, nullable` | | | | `data[].seller` | `string` | Yes | | | `data[].tao_amount` | `string, nullable` | | | | `data[].timestamp` | `string` | Yes | | | `data[].unlock_block` | `integer (int32), nullable` | | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Lockup Listings Paginated current lockup alpha listings. _Source: https://taostats.io/docs/new/otc/get-otc-lockup-listings_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/lockup/listings ``` Requires an API key in the `Authorization` header. Paginated current lockup alpha listings. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/lockup/listings" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/lockup/listings', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/lockup/listings", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `active`, `cancelled`, `filled`, or `all` (default). | | `price_offset_bps_min` | query | `integer (int32)` | | Minimum price offset (basis points, inclusive). | | `price_offset_bps_max` | query | `integer (int32)` | | Maximum price offset (basis points, inclusive). | | `lockup_duration_min` | query | `integer (int32)` | | Minimum lockup duration (blocks, inclusive). | | `lockup_duration_max` | query | `integer (int32)` | | Maximum lockup duration (blocks, inclusive). | | `total_amount_min` | query | `string` | | Minimum total amount (RAO, inclusive). | | `total_amount_max` | query | `string` | | Maximum total amount (RAO, inclusive). | | `remaining_amount_min` | query | `string` | | Minimum remaining amount (RAO, inclusive). | | `remaining_amount_max` | query | `string` | | Maximum remaining amount (RAO, inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of lockup alpha listings | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].created_block` | `integer (int32)` | Yes | Block the listing was created in. | | `data[].created_timestamp` | `string` | Yes | Creation time, ISO 8601 with millisecond precision. | | `data[].force_cancelled` | `boolean` | Yes | Whether the listing was force-cancelled. | | `data[].hotkey` | `string` | Yes | Hotkey the alpha is staked to, SS58 address. | | `data[].initiated_by` | `string, nullable` | | Account that initiated a cancellation, SS58 address (null otherwise). | | `data[].listing_id` | `string` | Yes | Listing ID. | | `data[].lockup_duration` | `integer (int32)` | Yes | Lockup duration in blocks. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price_offset_bps` | `integer (int32)` | Yes | Price offset in basis points. | | `data[].remaining_amount` | `string` | Yes | Remaining unsold alpha amount (RAO), as a decimal string. | | `data[].seller` | `string` | Yes | Seller coldkey, SS58 address. | | `data[].status` | `string` | Yes | `active`, `cancelled`, or `filled`. | | `data[].total_amount` | `string` | Yes | Total alpha amount listed (RAO), as a decimal string. | | `data[].updated_block` | `integer (int32)` | Yes | Block of the last update. | | `data[].updated_timestamp` | `string` | Yes | Last-update time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Lockup Purchases Paginated current lockup purchases. _Source: https://taostats.io/docs/new/otc/get-otc-lockup-purchases_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/lockup/purchases ``` Requires an API key in the `Authorization` header. Paginated current lockup purchases. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/lockup/purchases" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/lockup/purchases', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/lockup/purchases", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `purchase_id` | query | `string` | | Filter by purchase ID (exact match). | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `locked`, `claimed`, or `all` (default). | | `unlock_block_start` | query | `integer (int32)` | | Unlock-block range start (inclusive). | | `unlock_block_end` | query | `integer (int32)` | | Unlock-block range end (inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of lockup purchases | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string` | Yes | | | `data[].amount_claimed` | `string, nullable` | | | | `data[].buyer` | `string` | Yes | | | `data[].claimed_block` | `integer (int32), nullable` | | | | `data[].claimed_timestamp` | `string, nullable` | | | | `data[].created_block` | `integer (int32)` | Yes | | | `data[].created_timestamp` | `string` | Yes | | | `data[].escrow_account` | `string` | Yes | | | `data[].executed_price` | `string` | Yes | | | `data[].fee` | `string` | Yes | | | `data[].listing_id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].purchase_id` | `string` | Yes | | | `data[].seller` | `string` | Yes | | | `data[].status` | `string` | Yes | | | `data[].tao_amount` | `string` | Yes | | | `data[].unlock_block` | `integer (int32)` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Lockup Users Stats Paginated per-user lockup statistics. _Source: https://taostats.io/docs/new/otc/get-otc-lockup-users-stats_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/lockup/users/stats ``` Requires an API key in the `Authorization` header. Paginated per-user lockup statistics. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/lockup/users/stats" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/lockup/users/stats', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/lockup/users/stats", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `account` | query | `string` | | Filter by account (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `last_activity_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated per-user lockup statistics | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].account` | `string` | Yes | | | `data[].last_activity_block` | `integer (int32)` | Yes | | | `data[].last_activity_timestamp` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].total_claims_made` | `integer (int64)` | Yes | | | `data[].total_fees_paid` | `string` | Yes | | | `data[].total_listings_created` | `integer (int64)` | Yes | | | `data[].total_purchases_made` | `integer (int64)` | Yes | | | `data[].total_volume_bought_alpha` | `string` | Yes | | | `data[].total_volume_bought_tao` | `string` | Yes | | | `data[].total_volume_sold_alpha` | `string` | Yes | | | `data[].total_volume_sold_tao` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Offers History Paginated TAO-offer event history. _Source: https://taostats.io/docs/new/otc/get-otc-offers-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/offers/history ``` Requires an API key in the `Authorization` header. Paginated TAO-offer event history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/offers/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/offers/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/offers/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `event_type` | query | `string` | | `created`, `cancelled`, `taken`, or `all` (default). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated TAO-offer event history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string, nullable` | Yes | | | `data[].amount` | `string` | Yes | | | `data[].amount_returned` | `string, nullable` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string` | Yes | | | `data[].event_type` | `string` | Yes | | | `data[].executed_price` | `string, nullable` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string, nullable` | Yes | | | `data[].id` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].offer_id` | `string` | Yes | | | `data[].price_offset_bps` | `integer (int32)` | Yes | | | `data[].seller` | `string, nullable` | Yes | | | `data[].timestamp` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Offers Paginated current OTC TAO offers. _Source: https://taostats.io/docs/new/otc/get-otc-offers_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/offers ``` Requires an API key in the `Authorization` header. Paginated current OTC TAO offers. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/offers" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/offers', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/offers", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `status` | query | `string` | | `active`, `cancelled`, `taken`, or `all` (default). | | `price_offset_bps_min` | query | `integer (int32)` | | Minimum price offset (basis points, inclusive). | | `price_offset_bps_max` | query | `integer (int32)` | | Maximum price offset (basis points, inclusive). | | `amount_min` | query | `string` | | Minimum amount (RAO, inclusive). | | `amount_max` | query | `string` | | Maximum amount (RAO, inclusive). | | `created_block_start` | query | `integer (int32)` | | Created-block range start (inclusive). | | `created_block_end` | query | `integer (int32)` | | Created-block range end (inclusive). | | `created_timestamp_start` | query | `integer (int64)` | | Created-time range start, Unix seconds (inclusive). | | `created_timestamp_end` | query | `integer (int64)` | | Created-time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `created_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated list of OTC TAO offers | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | TAO amount (RAO), as a decimal string. | | `data[].buyer` | `string` | Yes | Buyer coldkey, SS58 address. | | `data[].created_block` | `integer (int32)` | Yes | Block the offer was created in. | | `data[].created_timestamp` | `string` | Yes | Creation time, ISO 8601 with millisecond precision. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].offer_id` | `string` | Yes | Offer ID. | | `data[].price_offset_bps` | `integer (int32)` | Yes | Price offset in basis points. | | `data[].status` | `string` | Yes | `active`, `cancelled`, or `taken`. | | `data[].updated_block` | `integer (int32)` | Yes | Block of the last update. | | `data[].updated_timestamp` | `string` | Yes | Last-update time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Subnets Status Paginated current OTC status per subnet. _Source: https://taostats.io/docs/new/otc/get-otc-subnets-status_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/subnets/status ``` Requires an API key in the `Authorization` header. Paginated current OTC status per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/subnets/status" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/subnets/status', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/subnets/status", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `frozen` | query | `string` | | `frozen`, `unfrozen`, or `all` (default). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated OTC subnet status | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block the status change landed in. | | `data[].changed_by` | `string` | Yes | Account that changed the status, SS58 address. | | `data[].frozen` | `boolean` | Yes | Whether OTC trading is frozen. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].reason` | `string` | Yes | Reason for the freeze/unfreeze. | | `data[].timestamp` | `string` | Yes | Change time, ISO 8601 with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Trades Paginated completed OTC trades. _Source: https://taostats.io/docs/new/otc/get-otc-trades_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/trades ``` Requires an API key in the `Authorization` header. Paginated completed OTC trades. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/trades" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/trades', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/trades", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `trade_type` | query | `string` | | `listing_taken`, `offer_taken`, or `all` (default). | | `seller` | query | `string` | | Filter by seller (SS58 or 0x-hex; normalized to SS58). | | `buyer` | query | `string` | | Filter by buyer (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `listing_id` | query | `string` | | Filter by listing ID (exact match). | | `offer_id` | query | `string` | | Filter by offer ID (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Time range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Time range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated completed OTC trades | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_amount` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].buyer` | `string` | Yes | | | `data[].executed_price` | `string` | Yes | | | `data[].extrinsic_id` | `string` | Yes | | | `data[].fee` | `string` | Yes | | | `data[].id` | `string` | Yes | | | `data[].listing_id` | `string, nullable` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].offer_id` | `string, nullable` | Yes | | | `data[].price_offset_bps` | `integer (int32)` | Yes | | | `data[].seller` | `string` | Yes | | | `data[].tao_amount` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | | | `data[].trade_type` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get OTC Users Stats Paginated per-user OTC statistics. _Source: https://taostats.io/docs/new/otc/get-otc-users-stats_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/otc/users/stats ``` Requires an API key in the `Authorization` header. Paginated per-user OTC statistics. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/otc/users/stats" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/otc/users/stats', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/otc/users/stats", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `account` | query | `string` | | Filter by account (SS58 or 0x-hex; normalized to SS58). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `last_activity_block`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. | ## Responses ### `200` — Paginated per-user OTC statistics | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].account` | `string` | Yes | | | `data[].last_activity_block` | `integer (int32)` | Yes | | | `data[].last_activity_timestamp` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].total_listings_created` | `integer (int64)` | Yes | | | `data[].total_offers_created` | `integer (int64)` | Yes | | | `data[].total_trades_as_buyer` | `integer (int64)` | Yes | | | `data[].total_trades_as_seller` | `integer (int64)` | Yes | | | `data[].total_volume_alpha` | `string` | Yes | | | `data[].total_volume_tao` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Price Every Taostats API endpoint in the Price group, with its method and path. _Source: https://taostats.io/docs/new/price_ _Last reviewed: 2026-10-07_ 4 Price endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Price History](https://taostats.io/docs/new/price/get-price-history) | `GET` | `/v1/price/history` | | [Get Price](https://taostats.io/docs/new/price/get-price) | `GET` | `/v1/price` | | [Get Price OHLC](https://taostats.io/docs/new/price/get-price-ohlc) | `GET` | `/v1/price/ohlc` | | [Get Price Simple](https://taostats.io/docs/new/price/get-price-simple) | `GET` | `/v1/price/simple` | --- # Get Price History Price observations over time for an asset. _Source: https://taostats.io/docs/new/price/get-price-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/price/history ``` Requires an API key in the `Authorization` header. Price observations over time for an asset. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/price/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/price/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/price/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `asset` | query | `string` | Yes | Asset symbol, e.g. `TAO`. Matched case-insensitively. | | `timestamp_start` | query | `integer (int64)` | | Range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Range end, unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so a page past the window is rejected with a 400. Reach deeper history with `timestamp_start` / `timestamp_end` rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated price history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].circulating_supply` | `string` | Yes | Circulating supply (decimal string). | | `data[].created_at` | `string` | Yes | When this point entered the series (ISO 8601). | | `data[].fully_diluted_market_cap` | `string` | Yes | Fully diluted market capitalisation in USD (decimal string). | | `data[].last_updated` | `string` | Yes | CoinMarketCap's own last-touched time for the asset (ISO 8601). This is an entry-level stamp, not the quote's; read `created_at` for when we recorded the row. | | `data[].market_cap` | `string` | Yes | Market capitalisation in USD (decimal string). | | `data[].market_cap_dominance` | `string` | Yes | Share of total crypto market capitalisation, percent (decimal string). | | `data[].max_supply` | `string` | Yes | Maximum supply (decimal string). | | `data[].name` | `string` | Yes | Asset name, e.g. `Bittensor`. | | `data[].percent_change_1h` | `string` | Yes | 1-hour price change, percent (decimal string). | | `data[].percent_change_24h` | `string` | Yes | 24-hour price change, percent (decimal string). | | `data[].percent_change_30d` | `string` | Yes | 30-day price change, percent (decimal string). | | `data[].percent_change_60d` | `string` | Yes | 60-day price change, percent (decimal string). | | `data[].percent_change_7d` | `string` | Yes | 7-day price change, percent (decimal string). | | `data[].percent_change_90d` | `string` | Yes | 90-day price change, percent (decimal string). | | `data[].price` | `string` | Yes | Price in USD (decimal string). | | `data[].slug` | `string` | Yes | Asset slug, e.g. `bittensor`. | | `data[].symbol` | `string` | Yes | Asset symbol, e.g. `TAO`. | | `data[].total_supply` | `string` | Yes | Total supply (decimal string). | | `data[].updated_at` | `string` | Yes | Row write time (ISO 8601). Equal to `created_at` for every row. | | `data[].volume_24h` | `string` | Yes | 24-hour trading volume in USD (decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Price Current price snapshot for an asset. _Source: https://taostats.io/docs/new/price/get-price_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/price ``` Requires an API key in the `Authorization` header. Current price snapshot for an asset. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/price" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/price', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/price", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `asset` | query | `string` | Yes | Asset symbol, e.g. `TAO`. Matched case-insensitively. | ## Responses ### `200` — Latest price observation for the asset | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | The snapshot, as a one-element array: `data` is always an array (`docs/api_standards.md`). | | `data[].circulating_supply` | `string` | Yes | Circulating supply (decimal string). | | `data[].created_at` | `string` | Yes | When this point entered the series (ISO 8601). | | `data[].fully_diluted_market_cap` | `string` | Yes | Fully diluted market capitalisation in USD (decimal string). | | `data[].last_updated` | `string` | Yes | CoinMarketCap's own last-touched time for the asset (ISO 8601). This is an entry-level stamp, not the quote's; read `created_at` for when we recorded the row. | | `data[].market_cap` | `string` | Yes | Market capitalisation in USD (decimal string). | | `data[].market_cap_dominance` | `string` | Yes | Share of total crypto market capitalisation, percent (decimal string). | | `data[].max_supply` | `string` | Yes | Maximum supply (decimal string). | | `data[].name` | `string` | Yes | Asset name, e.g. `Bittensor`. | | `data[].percent_change_1h` | `string` | Yes | 1-hour price change, percent (decimal string). | | `data[].percent_change_24h` | `string` | Yes | 24-hour price change, percent (decimal string). | | `data[].percent_change_30d` | `string` | Yes | 30-day price change, percent (decimal string). | | `data[].percent_change_60d` | `string` | Yes | 60-day price change, percent (decimal string). | | `data[].percent_change_7d` | `string` | Yes | 7-day price change, percent (decimal string). | | `data[].percent_change_90d` | `string` | Yes | 90-day price change, percent (decimal string). | | `data[].price` | `string` | Yes | Price in USD (decimal string). | | `data[].slug` | `string` | Yes | Asset slug, e.g. `bittensor`. | | `data[].symbol` | `string` | Yes | Asset symbol, e.g. `TAO`. | | `data[].total_supply` | `string` | Yes | Total supply (decimal string). | | `data[].updated_at` | `string` | Yes | Row write time (ISO 8601). Equal to `created_at` for every row. | | `data[].volume_24h` | `string` | Yes | 24-hour trading volume in USD (decimal string). | | `pagination` | `object` | Yes | Pagination block. One item on one page, or zero items for an asset the series does not carry. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Price OHLC OHLC candles for an asset, aggregated by period. _Source: https://taostats.io/docs/new/price/get-price-ohlc_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/price/ohlc ``` Requires an API key in the `Authorization` header. OHLC candles for an asset, aggregated by period. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/price/ohlc" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/price/ohlc', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/price/ohlc", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `asset` | query | `string` | Yes | Asset symbol, e.g. `TAO`. Matched case-insensitively. | | `period` | query | `string` | Yes | Candle period: `1m`, `1h` or `1d`. One of `1m`, `1h`, `1d`. | | `timestamp_start` | query | `integer (int64)` | | Range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Range end, unix seconds (**exclusive**) — an observation recorded exactly at this second belongs to the next candle, so a range ending on a period boundary does not open a one-row candle beyond it. This matches the OLD API's `/api/price/ohlc/v1`, whose upper bound is also exclusive while `/api/price/history/v1`'s is inclusive. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so a page past the window is rejected with a 400. Reach deeper history with `timestamp_start` / `timestamp_end` rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | ## Responses ### `200` — Paginated OHLC candles, newest first | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].asset` | `string` | Yes | Asset symbol, echoed back. | | `data[].close` | `string` | Yes | Last price in the period (decimal string, four decimal places). | | `data[].high` | `string` | Yes | Highest price in the period (decimal string, four decimal places). | | `data[].low` | `string` | Yes | Lowest price in the period (decimal string, four decimal places). | | `data[].open` | `string` | Yes | First price in the period (decimal string, four decimal places). | | `data[].period` | `string` | Yes | The requested period: `1m`, `1h` or `1d`. | | `data[].timestamp` | `string` | Yes | Start of the period (ISO 8601). | | `data[].volume_24h` | `string` | Yes | 24-hour volume reported by the period's closing observation (decimal string, four decimal places). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Price Simple The current TAO price, and nothing else. _Source: https://taostats.io/docs/new/price/get-price-simple_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/price/simple ``` Requires an API key in the `Authorization` header. The current TAO price, and nothing else. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/price/simple" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/price/simple', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/price/simple", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest TAO price | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | The price, as a one-element array. | | `data[].price` | `string` | Yes | TAO price in USD (decimal string). | | `data[].timestamp` | `string` | Yes | When the price was recorded (ISO 8601). | | `pagination` | `object` | Yes | Pagination block. Always one item on one page. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # RPC Every Taostats API endpoint in the RPC group, with its method and path. _Source: https://taostats.io/docs/new/rpc_ _Last reviewed: 2026-10-07_ One RPC endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Post RPC Http](https://taostats.io/docs/new/rpc/post-rpc-http) | `POST` | `/v1/rpc/http` | --- # Post RPC Http Forward a JSON-RPC 2.0 request (object or batch array) to the default finney_lite node and relay its response. _Source: https://taostats.io/docs/new/rpc/post-rpc-http_ _Last reviewed: 2026-10-07_ ```http POST https://api.taostats.io/v1/rpc/http ``` Requires an API key in the `Authorization` header. Forward a JSON-RPC 2.0 request (object or batch array) to the default finney_lite node and relay its response. A WebSocket variant exists at `WS /v1/rpc/ws/{target}` (targets: `finney_lite`, `finney_archive`) — it is not listed as its own OpenAPI path because OpenAPI 3 cannot describe WebSocket upgrades. ## Code samples **cURL** ```bash curl -X POST \ -H "Authorization: " \ -H "Content-Type: application/json" \ -d '{}' \ "https://api.taostats.io/v1/rpc/http" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/rpc/http', { method: 'POST', headers: { Authorization: '', 'Content-Type': 'application/json', }, body: JSON.stringify({}), }); const data = await response.json(); ``` **Python** ```python import requests response = requests.post( "https://api.taostats.io/v1/rpc/http", headers={"Authorization": ""}, json={}, ) data = response.json() ``` ## Parameters _No parameters._ ## Request body A JSON-RPC 2.0 request object or batch array _No documented fields._ ```json {} ``` ## Responses ### `200` — The upstream node's JSON-RPC response Returns `application/json` — a `object` body. ### `400` — Body is not valid JSON or not a JSON-RPC request object or array | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — Request body exceeds the size limit | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `415` — Content type is not application/json | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Upstream node unreachable or returned a non-JSON body | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Status Every Taostats API endpoint in the Status group, with its method and path. _Source: https://taostats.io/docs/new/status_ _Last reviewed: 2026-10-07_ One Status endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Get Status](https://taostats.io/docs/new/status/get-status) | `GET` | `/v1/status` | --- # Get Status Service health, API version, and current server time. _Source: https://taostats.io/docs/new/status/get-status_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/status ``` Requires an API key in the `Authorization` header. Service health, API version, and current server time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/status" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/status', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/status", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Service is healthy | Field | Type | Required | Description | | --- | --- | --- | --- | | `ok` | `boolean` | Yes | Service health status. Always `true` while the process is serving. | | `timestamp` | `string` | Yes | Current server time as an ISO 8601 string with millisecond precision and a trailing `Z`, e.g. `2024-05-15T10:30:00.000Z`. | | `version` | `string` | Yes | API version, taken from `Cargo.toml` at compile time (e.g. `0.1.0`). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Subnets Every Taostats API endpoint in the Subnets group, with its method and path. _Source: https://taostats.io/docs/new/subnets_ _Last reviewed: 2026-10-07_ 34 Subnets endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Subnets History](https://taostats.io/docs/new/subnets/get-subnets-history) | `GET` | `/v1/subnets/history` | | [Get Subnets Burns](https://taostats.io/docs/new/subnets/get-subnets-burns) | `GET` | `/v1/subnets/burns` | | [Get Subnets Burns Daily](https://taostats.io/docs/new/subnets/get-subnets-burns-daily) | `GET` | `/v1/subnets/burns/daily` | | [Get Subnets Burns Total](https://taostats.io/docs/new/subnets/get-subnets-burns-total) | `GET` | `/v1/subnets/burns/total` | | [Get Subnets Conviction History](https://taostats.io/docs/new/subnets/get-subnets-conviction-history) | `GET` | `/v1/subnets/conviction/history` | | [Get Subnets Conviction](https://taostats.io/docs/new/subnets/get-subnets-conviction) | `GET` | `/v1/subnets/conviction` | | [Get Subnets Deregistrations History](https://taostats.io/docs/new/subnets/get-subnets-deregistrations-history) | `GET` | `/v1/subnets/deregistrations/history` | | [Get Subnets Deregistrations](https://taostats.io/docs/new/subnets/get-subnets-deregistrations) | `GET` | `/v1/subnets/deregistrations` | | [Get Subnets Distribution Coldkey](https://taostats.io/docs/new/subnets/get-subnets-distribution-coldkey) | `GET` | `/v1/subnets/distribution/coldkey` | | [Get Subnets Distribution Incentive](https://taostats.io/docs/new/subnets/get-subnets-distribution-incentive) | `GET` | `/v1/subnets/distribution/incentive` | | [Get Subnets Distribution Ip](https://taostats.io/docs/new/subnets/get-subnets-distribution-ip) | `GET` | `/v1/subnets/distribution/ip` | | [Get Subnets Epochs](https://taostats.io/docs/new/subnets/get-subnets-epochs) | `GET` | `/v1/subnets/epochs` | | [Get Subnets Hyperparameters](https://taostats.io/docs/new/subnets/get-subnets-hyperparameters) | `GET` | `/v1/subnets/hyperparameters` | | [Get Subnets Identities History](https://taostats.io/docs/new/subnets/get-subnets-identities-history) | `GET` | `/v1/subnets/identities/history` | | [Get Subnets Identities](https://taostats.io/docs/new/subnets/get-subnets-identities) | `GET` | `/v1/subnets/identities` | | [Get Subnets Metagraph History](https://taostats.io/docs/new/subnets/get-subnets-metagraph-history) | `GET` | `/v1/subnets/metagraph/history` | | [Get Subnets Metagraph](https://taostats.io/docs/new/subnets/get-subnets-metagraph) | `GET` | `/v1/subnets/metagraph` | | [Get Subnets Metagraph Aggregate History](https://taostats.io/docs/new/subnets/get-subnets-metagraph-aggregate-history) | `GET` | `/v1/subnets/metagraph/aggregate/history` | | [Get Subnets Metagraph Aggregate](https://taostats.io/docs/new/subnets/get-subnets-metagraph-aggregate) | `GET` | `/v1/subnets/metagraph/aggregate` | | [Get Subnets Metrics](https://taostats.io/docs/new/subnets/get-subnets-metrics) | `GET` | `/v1/subnets/metrics` | | [Get Subnets Neuron Deregistration Events](https://taostats.io/docs/new/subnets/get-subnets-neuron-deregistration-events) | `GET` | `/v1/subnets/neuron-deregistration-events` | | [Get Subnets Neuron Registration Events](https://taostats.io/docs/new/subnets/get-subnets-neuron-registration-events) | `GET` | `/v1/subnets/neuron-registration-events` | | [Get Subnets Owners](https://taostats.io/docs/new/subnets/get-subnets-owners) | `GET` | `/v1/subnets/owners` | | [Get Subnets Pools History](https://taostats.io/docs/new/subnets/get-subnets-pools-history) | `GET` | `/v1/subnets/pools/history` | | [Get Subnets Pools](https://taostats.io/docs/new/subnets/get-subnets-pools) | `GET` | `/v1/subnets/pools` | | [Get Subnets Pools Aggregate](https://taostats.io/docs/new/subnets/get-subnets-pools-aggregate) | `GET` | `/v1/subnets/pools/aggregate` | | [Get Subnets Pools Total Price History](https://taostats.io/docs/new/subnets/get-subnets-pools-total-price-history) | `GET` | `/v1/subnets/pools/total-price/history` | | [Get Subnets Pools Total Price](https://taostats.io/docs/new/subnets/get-subnets-pools-total-price) | `GET` | `/v1/subnets/pools/total-price` | | [Get Subnets Registration Cost History](https://taostats.io/docs/new/subnets/get-subnets-registration-cost-history) | `GET` | `/v1/subnets/registration-cost/history` | | [Get Subnets Registration Cost](https://taostats.io/docs/new/subnets/get-subnets-registration-cost) | `GET` | `/v1/subnets/registration-cost` | | [Get Subnets Registrations](https://taostats.io/docs/new/subnets/get-subnets-registrations) | `GET` | `/v1/subnets/registrations` | | [Get Subnets Stake Events](https://taostats.io/docs/new/subnets/get-subnets-stake-events) | `GET` | `/v1/subnets/stake-events` | | [Get Subnets Swaps](https://taostats.io/docs/new/subnets/get-subnets-swaps) | `GET` | `/v1/subnets/swaps` | | [Get Subnets Trades](https://taostats.io/docs/new/subnets/get-subnets-trades) | `GET` | `/v1/subnets/trades` | --- # Get Subnets History One subnet's statistics over time. _Source: https://taostats.io/docs/new/subnets/get-subnets-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/history ``` Requires an API key in the `Authorization` header. One subnet's statistics over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000; a page past it is rejected with a 400. Reach deeper history with the range filters instead. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by: `timestamp` (default) or `block_number`. Both give the same order. One of `timestamp`, `block_number`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `frequency` | query | `string` | | How far apart the points are. Default: `by_day`. `by_day` returns blocks that are multiples of 7,200, `by_hour` multiples of 300, and `by_block` every stored snapshot, which is every 300th block — not every block. One of `by_block`, `by_hour`, `by_day`. | ## Responses ### `200` — One subnet's statistics over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].emission` | `string` | Yes | TAO injected into the subnet's pool on this block (`SubnetTaoInEmission`), RAO. | | `data[].excess_tao` | `string` | Yes | Excess TAO the chain bought into the subnet on this block (`SubnetExcessTao`), RAO. `"0"` before spec 411 (block 8,283,784), where the chain did not have it. | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].neuron_registration_cost` | `string` | Yes | The subnet's registration cost at this block (`Burn`), RAO. | | `data[].recycled_24_hours` | `string, nullable` | | TAO recycled by registrations over the previous 7,200 blocks, RAO, never negative. `null` when there is no snapshot 7,200 blocks earlier. Root: the OLD API's figure — `"0"`, and on dTAO's first day OLD's own non-zero figures. | | `data[].timestamp` | `string` | Yes | ISO 8601, millisecond precision, UTC. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Burns Alpha burns made by an extrinsic. _Source: https://taostats.io/docs/new/subnets/get-subnets-burns_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/burns ``` Requires an API key in the `Authorization` header. Alpha burns made by an extrinsic. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/burns" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/burns', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/burns", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `extrinsic_id` | query | `string` | | Filter by extrinsic ID. | | `burn_type` | query | `string` | | Filter by burn type. `call` is the only accepted value, and it is what every row here already is, so passing it changes nothing. One of `call`. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of alpha burns made by an extrinsic | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Burn amount (RAO, u64 as decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].burn_type` | `string` | Yes | Always `call`. This endpoint serves burns made by an extrinsic and nothing else; the field is kept so existing parsing does not break. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58) that paid for the burn. | | `data[].extrinsic_id` | `string, nullable` | Yes | Extrinsic ID, or `null`. Null is rare but real: `do_burn_alpha` needs a signed origin, which the scheduler can supply from `on_initialize` with no extrinsic at all, so a burn a caller set up can land with no extrinsic to point at. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Burns Daily The chain's cumulative burn counter turned into one bar per UTC day for one subnet. _Source: https://taostats.io/docs/new/subnets/get-subnets-burns-daily_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/burns/daily ``` Requires an API key in the `Authorization` header. The chain's cumulative burn counter turned into one bar per UTC day for one subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/burns/daily" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/burns/daily', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/burns/daily", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID. Required — the series is per subnet. | | `days` | query | `integer (int32)` | | How many whole UTC days to return, ending today. Default 30, max 36,500. | ## Responses ### `200` — Alpha burned per UTC day for one subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string, nullable` | Yes | Alpha burned **during** this UTC day (RAO, u64 as a decimal string). **`null` means "not knowable", never "nothing was burned".** It is null in exactly four cases, all of them real: the previous day has no row, so there is nothing to difference against; the counter *fell*, which is the chain clearing it at the end of a subnet generation; the day contains the spec-446 rebase; or the subnet was registered between the two days' sampled blocks, which restarts the counter. A zero here is a genuine zero. | | `data[].block_number` | `integer (int32)` | Yes | The block the cumulative figure was read at — the last block of this day that the snapshot table holds. Reported so a disputed number can be traced to one row rather than argued about. | | `data[].date` | `string` | Yes | The UTC day this bucket covers, `YYYY-MM-DD`. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp of that block, millisecond precision. | | `data[].total_alpha_burned` | `string` | Yes | The chain's cumulative counter at that block (RAO, u64 as a decimal string). This is the same column `/v1/subnets/burns/total` serves, `subnet_pool_v1.total_alpha_burned`, copied from that day's last row into `subnet_pool_by_utc_day_v1`: by the indexer, in the same batch as the history row, and for days before the indexer wrote the table, by `backfill_subnet_pool_by_utc_day`. The indexer's two inserts run concurrently and neither is guaranteed to become visible first, so the current day's value can differ from the total, in either direction, for that moment. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Burns Total The chain's cumulative burn counter per subnet, served as it stands. _Source: https://taostats.io/docs/new/subnets/get-subnets-burns-total_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/burns/total ``` Requires an API key in the `Authorization` header. The chain's cumulative burn counter per subnet, served as it stands. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/burns/total" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/burns/total', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/burns/total", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — The chain's cumulative burned alpha per subnet, uncorrected | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Total burned amount (RAO, u64 as decimal string). The chain's own cumulative counter, `AlphaAssets::AlphaBurned`, served uncorrected. It counts every burn — the ones a caller made and the ones the runtime made on its own — so it is larger than the sum of the rows `/v1/subnets/burns` lists, and it can fall as well as rise: the chain clears a subnet's counter when a subnet generation ends. | | `data[].block_number` | `integer (int32)` | Yes | Block height of the latest snapshot. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Conviction History Conviction data over time. _Source: https://taostats.io/docs/new/subnets/get-subnets-conviction-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/conviction/history ``` Requires an API key in the `Authorization` header. Conviction data over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/conviction/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/conviction/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/conviction/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Conviction data over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount_locked` | `string` | Yes | Rolled-forward locked alpha (base units), as a string. | | `data[].amount_tao` | `string` | Yes | TAO value of the locked alpha (RAO), as a string. | | `data[].block_number` | `integer (int32)` | Yes | Snapshot block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].conviction` | `string` | Yes | Rolled-forward conviction score (base units), as a string. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].is_owner_coldkey` | `boolean` | Yes | Whether this lock's coldkey is the subnet owner coldkey. | | `data[].is_owner_hotkey` | `boolean` | Yes | Whether this lock's hotkey is the subnet owner hotkey. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].perpetual` | `boolean` | Yes | Whether the lock is perpetual (non-decaying). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Conviction Current conviction-lock snapshot. _Source: https://taostats.io/docs/new/subnets/get-subnets-conviction_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/conviction ``` Requires an API key in the `Authorization` header. Current conviction-lock snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/conviction" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/conviction', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/conviction", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `amount_locked`. One of `amount_locked`, `amount_tao`, `conviction`, `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Current conviction data | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount_locked` | `string` | Yes | Rolled-forward locked alpha (base units), as a string. | | `data[].amount_tao` | `string` | Yes | TAO value of the locked alpha (RAO), as a string. | | `data[].block_number` | `integer (int32)` | Yes | Snapshot block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].conviction` | `string` | Yes | Rolled-forward conviction score (base units), as a string. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].is_owner_coldkey` | `boolean` | Yes | Whether this lock's coldkey is the subnet owner coldkey. | | `data[].is_owner_hotkey` | `boolean` | Yes | Whether this lock's hotkey is the subnet owner hotkey. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].perpetual` | `boolean` | Yes | Whether the lock is perpetual (non-decaying). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Deregistrations History Subnet pruning-rank time series. _Source: https://taostats.io/docs/new/subnets/get-subnets-deregistrations-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/deregistrations/history ``` Requires an API key in the `Authorization` header. Subnet pruning-rank time series. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/deregistrations/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/deregistrations/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/deregistrations/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Subnet pruning-rank snapshots over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].immunity_blocks_remaining` | `integer (int32)` | Yes | Blocks of immunity remaining (0 once immunity has lapsed). | | `data[].is_immune` | `boolean` | Yes | Whether the subnet is currently immune from pruning. | | `data[].moving_price` | `string` | Yes | Moving price as the raw `I96F32` bits (price × 2³²), decimal string. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].rank` | `integer (int32)` | Yes | Deregistration rank (1 = next to prune). Includes immune subnets, interleaved purely by price. | | `data[].registered_at_block` | `integer (int32)` | Yes | Block at which the subnet was registered. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Deregistrations Latest subnet pruning-rank snapshot. _Source: https://taostats.io/docs/new/subnets/get-subnets-deregistrations_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/deregistrations ``` Requires an API key in the `Authorization` header. Latest subnet pruning-rank snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/deregistrations" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/deregistrations', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/deregistrations", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `is_immune` | query | `boolean` | | Filter by immunity status. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `rank`. One of `rank`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Latest subnet pruning-rank snapshot | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].immunity_blocks_remaining` | `integer (int32)` | Yes | Blocks of immunity remaining (0 once immunity has lapsed). | | `data[].is_immune` | `boolean` | Yes | Whether the subnet is currently immune from pruning. | | `data[].moving_price` | `string` | Yes | Moving price as the raw `I96F32` bits (price × 2³²), decimal string. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].rank` | `integer (int32)` | Yes | Deregistration rank (1 = next to prune). Includes immune subnets, interleaved purely by price. | | `data[].registered_at_block` | `integer (int32)` | Yes | Block at which the subnet was registered. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Distribution Coldkey GET /v1/subnets/distribution/coldkey — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-distribution-coldkey_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/distribution/coldkey ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/distribution/coldkey" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/distribution/coldkey', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/distribution/coldkey", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | ## Responses ### `200` — Coldkey distribution for a subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].count` | `integer (int32)` | Yes | Number of UIDs controlled by this coldkey. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Distribution Incentive GET /v1/subnets/distribution/incentive — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-distribution-incentive_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/distribution/incentive ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/distribution/incentive" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/distribution/incentive', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/distribution/incentive", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | ## Responses ### `200` — Incentive distribution for a subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].incentive` | `string` | Yes | Incentive value (decimal string): recall's split-weighted incentive, normalised to `0..=1`, as the OLD API serves it (#1068). On a single-mechanism subnet it is the plain `u16 / 65535`. | | `data[].is_immunity_period` | `boolean` | Yes | Whether the neuron is within its immunity period. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Distribution Ip GET /v1/subnets/distribution/ip — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-distribution-ip_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/distribution/ip ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/distribution/ip" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/distribution/ip', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/distribution/ip", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | ## Responses ### `200` — IP distribution for a subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].count` | `integer (int32)` | Yes | Number of UIDs served on this IP. | | `data[].ip` | `string` | Yes | Axon IP address. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Epochs Per-block per-subnet emission data. _Source: https://taostats.io/docs/new/subnets/get-subnets-epochs_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/epochs ``` Requires an API key in the `Authorization` header. Per-block per-subnet emission data. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/epochs" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/epochs', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/epochs", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Per-block per-subnet emission data | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_in_emission` | `string` | Yes | Alpha injected into the pool this emission (RAO, u64 as string). | | `data[].alpha_out_emission` | `string` | Yes | Alpha emitted to miners/validators this block (RAO, u64 as string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].owner_cut` | `string` | Yes | Accumulated owner cut awaiting drain (RAO, u64 as string). | | `data[].root_alpha_divs` | `string` | Yes | Accumulated root alpha dividends awaiting drain (RAO, u64 as string). | | `data[].server_emission` | `string` | Yes | Accumulated miner alpha awaiting epoch drain (RAO, u64 as string). | | `data[].tao_in_emission` | `string` | Yes | TAO injected into the pool this emission (RAO, u64 as string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].validator_emission` | `string` | Yes | Accumulated validator alpha awaiting epoch drain (RAO, u64 as string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Hyperparameters Latest hyperparameters for one subnet, or for every subnet when netuid is omitted. _Source: https://taostats.io/docs/new/subnets/get-subnets-hyperparameters_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/hyperparameters ``` Requires an API key in the `Authorization` header. Latest hyperparameters for one subnet, or for every subnet when `netuid` is omitted. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/hyperparameters" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/hyperparameters', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/hyperparameters", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Subnet ID. Omit it to list every subnet in the latest snapshot. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50 without `netuid`, 1 with it. Max: 200. | ## Responses ### `200` — Subnet hyperparameters: one subnet, or every subnet in the latest snapshot | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].activity_cutoff` | `integer (int32)` | Yes | Blocks of inactivity before a neuron is considered inactive — the **effective** cutoff the runtime actually applies. From spec 423 the chain deprecated the absolute `ActivityCutoff` storage item and derives this as `max(1, activity_cutoff_factor_milli * tempo / 1000)`. Before #464 this field served the stale deprecated value, which was wrong for every subnet with a non-default tempo or factor. | | `data[].activity_cutoff_factor_milli` | `integer (int32)` | Yes | `ActivityCutoffFactorMilli` — per-mille of `tempo`; the hyperparameter that actually drives `activity_cutoff` from spec 423. `0` on snapshots below spec 423, where the chain item did not exist (the chain's own legal range is 1000–50000, so `0` is unambiguous). | | `data[].adjustment_alpha` | `string` | Yes | EMA weight for registration adjustment (u64 as string). | | `data[].adjustment_interval` | `integer (int32)` | Yes | Blocks between difficulty/burn auto-adjustments. | | `data[].alpha_high` | `string` | Yes | Upper bound for liquid alpha (float 0-1). | | `data[].alpha_low` | `string` | Yes | Lower bound for liquid alpha (float 0-1). | | `data[].alpha_sigmoid_steepness` | `integer (int32)` | Yes | Sigmoid steepness for the liquid-alpha curve. | | `data[].block_number` | `integer (int32)` | Yes | Block at which this snapshot was taken. | | `data[].bonds_moving_average` | `string` | Yes | EMA decay for bonds (float 0-1, e.g. `"0.9"`). | | `data[].bonds_penalty` | `string` | Yes | Interpolation weight between raw and clipped weights (float 0-1). | | `data[].bonds_reset_enabled` | `boolean` | Yes | Whether bonds reset on re-registration. | | `data[].burn_half_life` | `integer (int32)` | Yes | Tempos for burn to halve back toward `min_burn`. | | `data[].burn_increase_mult` | `string` | Yes | Burn increase multiplier (decimal string, range 1-3). | | `data[].collateral_drain_ratio` | `string` | Yes | Alpha of locked collateral released per alpha of miner incentive earned (decimal string). Defaults to `"1"`, not `"0"`. | | `data[].collateral_lock_share` | `string` | Yes | Share of the burned-registration price locked as miner collateral instead of burned, as a fraction (`collateral_lock_share / u16::MAX`). `"0"` means the collateral mechanism is off for this subnet, which is the default and the only value before spec 435. | | `data[].commit_reveal_period` | `string` | Yes | Epochs for the commit-reveal window (u64 as string). | | `data[].commit_reveal_weights_enabled` | `boolean` | Yes | Whether the commit-reveal weight protocol is active. | | `data[].difficulty` | `string` | Yes | Raw PoW difficulty target (u64 as string). | | `data[].immunity_period` | `integer (int32)` | Yes | Blocks a new neuron is immune from deregistration. | | `data[].kappa` | `string` | Yes | Consensus majority threshold (float 0-1, e.g. `"0.5"`). | | `data[].liquid_alpha_enabled` | `boolean` | Yes | Whether liquid alpha is enabled. | | `data[].max_allowed_uids` | `integer (int32)` | Yes | Maximum UIDs the subnet supports. | | `data[].max_allowed_validators` | `integer (int32)` | Yes | Maximum validator count. | | `data[].max_burn` | `string` | Yes | Maximum registration burn in RAO (u64 as string). | | `data[].max_difficulty` | `string` | Yes | Maximum PoW difficulty ceiling (u64 as string). | | `data[].max_registrations_per_block` | `integer (int32)` | Yes | Hard cap on registrations per block. | | `data[].max_weights_limit` | `integer (int32)` | Yes | Max weights limit — always 65535. | | `data[].mechanism_count` | `integer (int32)` | Yes | Number of active consensus mechanisms. | | `data[].mechanism_emission_split` | `array` | Yes | Emission split between mechanisms. | | `data[].mechanism_emission_split[]` | `integer (int32)` | Yes | | | `data[].min_allowed_uids` | `integer (int32)` | Yes | Minimum UIDs the subnet must support. | | `data[].min_allowed_weights` | `integer (int32)` | Yes | Minimum weight count per neuron. | | `data[].min_burn` | `string` | Yes | Minimum registration burn in RAO (u64 as string). | | `data[].min_difficulty` | `string` | Yes | Minimum PoW difficulty floor (u64 as string). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. Added with the all-subnets listing (#1250), where it is the only way to tell the rows apart; a number, as the OLD API's `/api/subnet/latest/v1` serves it. | | `data[].owner_immune_neuron_limit` | `integer (int32)` | Yes | Owner neurons immune from deregistration. | | `data[].pow_registration_allowed` | `boolean` | Yes | PoW registration flag (always disabled on dTAO-era subnets). | | `data[].recycle_or_burn` | `string` | Yes | Whether registration fees are recycled or burned (`"Recycle"` or `"Burn"`). | | `data[].registration_allowed` | `boolean` | Yes | Master switch for registration. | | `data[].rho` | `integer (int32)` | Yes | Raw scaling parameter `Rho`. | | `data[].root_claim_threshold` | `string` | Yes | Root claim eligibility threshold (decimal string). | | `data[].scaling_law_power` | `string` | Yes | Legacy scaling law power (float 0-1). | | `data[].serving_rate_limit` | `string` | Yes | Minimum blocks between axon serving updates (u64 as string). | | `data[].subnet_is_active` | `boolean` | Yes | Whether subtoken trading is enabled. | | `data[].target_regs_per_interval` | `integer (int32)` | Yes | Target registration count per adjustment interval. | | `data[].tempo` | `integer (int32)` | Yes | Blocks per epoch. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].transfers_enabled` | `boolean` | Yes | Whether alpha token transfers are enabled. | | `data[].voting_power_ema_alpha` | `string` | Yes | Voting power EMA alpha (float 0-1). | | `data[].voting_power_tracking` | `boolean` | Yes | Whether voting power tracking is enabled. | | `data[].weights_rate_limit` | `string` | Yes | Minimum blocks between weight-setting transactions (u64 as string). | | `data[].weights_version` | `string` | Yes | Version key for weight validation (u64 as string). | | `data[].yuma_version` | `integer (int32)` | Yes | Yuma consensus version (`2` or `3`). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Identities History Every identity a subnet's owner set. _Source: https://taostats.io/docs/new/subnets/get-subnets-identities-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/identities/history ``` Requires an API key in the `Authorization` header. Every identity a subnet's owner set. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/identities/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/identities/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/identities/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `owner` | query | `string` | | Filter by the coldkey that set the identity (SS58 or 0x-hex). | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000; a page past it is rejected with a 400. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `block_number`. One of `block_number`, `timestamp`, `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of subnet identity sets | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].additional` | `string, nullable` | | Additional information. | | `data[].block_number` | `integer (int32)` | Yes | Block the identity was set in. | | `data[].description` | `string, nullable` | | Subnet description. | | `data[].discord` | `string, nullable` | | Discord link. | | `data[].github_repo` | `string, nullable` | | GitHub repository URL. | | `data[].logo_url` | `string, nullable` | | Logo image URL; `null` for every set made before the runtime added the field (spec 290, block 5,947,548) and for a set that left it empty. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].owner` | `string` | Yes | Coldkey that set the identity (SS58): the call's origin, which for a call made through a proxy or multisig is the subnet owner's account, not the signer's. | | `data[].subnet_contact` | `string, nullable` | | Contact information. | | `data[].subnet_name` | `string` | Yes | Subnet name as set; `"Unknown"` when set empty. | | `data[].subnet_url` | `string, nullable` | | Subnet URL. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Identities Latest identity snapshot per subnet. _Source: https://taostats.io/docs/new/subnets/get-subnets-identities_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/identities ``` Requires an API key in the `Authorization` header. Latest identity snapshot per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/identities" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/identities', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/identities", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | ## Responses ### `200` — Latest identity snapshot per subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].additional` | `string, nullable` | | Additional information. | | `data[].description` | `string, nullable` | | Subnet description. | | `data[].discord` | `string, nullable` | | Discord link. | | `data[].github_repo` | `string, nullable` | | GitHub repository URL. | | `data[].logo_url` | `string, nullable` | | Logo image URL. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].subnet_contact` | `string, nullable` | | Contact information. | | `data[].subnet_name` | `string` | Yes | Subnet name. `"Root"` for netuid 0; `"Unknown"` for active subnets with no on-chain identity. | | `data[].subnet_url` | `string, nullable` | | Subnet URL. | | `data[].summary` | `string, nullable` | | Summary text (LLM-generated; always null until supplement table is wired). | | `data[].tags` | `array, nullable` | | Category tags (LLM-generated; always null until supplement table is wired). | | `data[].tags[]` | `string` | | | | `data[].twitter` | `string, nullable` | | Twitter handle (LLM-generated; always null until supplement table is wired). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Metagraph History Per-neuron snapshots over time. _Source: https://taostats.io/docs/new/subnets/get-subnets-metagraph-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/metagraph/history ``` Requires an API key in the `Authorization` header. Per-neuron snapshots over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/metagraph/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/metagraph/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/metagraph/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | | `uid` | query | `integer (int32)` | | Filter by neuron UID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `has_incentive` | query | `boolean` | | Keep only neurons with positive incentive (`false`: only neurons with none). The same test as `/v1/subnets/metagraph`'s `has_incentive`: the served, weighted `incentive`. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Per-neuron metagraph snapshots over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].active` | `boolean` | Yes | Whether the neuron is active. | | `data[].alpha_stake` | `string` | Yes | Alpha stake (RAO, `u64` decimal string): the chain's inherited alpha stake at the row's block, the snapshot row's `alpha_stake`. | | `data[].axon` | `string, nullable` | Yes | Axon `ip:port` (`null` when no axon is set). | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].collateral_earned_alpha` | `string` | Yes | Emission earned while this collateral entry has existed (alpha-RAO decimal string) — the progress meter against the lock, since release is `drain_ratio × emission`. Not a lifetime-of-hotkey total. | | `data[].consensus` | `string` | Yes | Consensus score (normalised `u16 / 65535`). | | `data[].daily_burned_alpha` | `string, nullable` | Yes | Daily burned alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_burned_alpha_as_tao` | `string, nullable` | Yes | Daily burned alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_alpha` | `string, nullable` | Yes | Daily mining alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_alpha_as_tao` | `string, nullable` | Yes | Daily mining alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_tao` | `string, nullable` | Yes | Daily mining TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_owner_alpha` | `string, nullable` | Yes | Daily owner alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_owner_alpha_as_tao` | `string, nullable` | Yes | Daily owner alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_total_rewards_as_tao` | `string, nullable` | Yes | Daily total rewards as TAO (RAO decimal string; always present). | | `data[].daily_validating_alpha` | `string, nullable` | Yes | Daily validating alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_validating_alpha_as_tao` | `string, nullable` | Yes | Daily validating alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_validating_tao` | `string, nullable` | Yes | Daily validating TAO (RAO decimal string; `null` when not applicable). | | `data[].dividends` | `string` | Yes | Dividends (normalised `u16 / 65535`). | | `data[].emission` | `string` | Yes | Emission (RAO, `u64` decimal string). | | `data[].free_alpha` | `string` | Yes | The hotkey's own alpha not frozen by collateral: `hotkey_alpha - locked_alpha`, floored at `"0"` (alpha-RAO decimal string). Read this, not `alpha_stake`, as "withdrawable at this UID". Per-*position* (coldkey, hotkey, netuid) withdrawable lives on `/v1/alpha/*`. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].hotkey_alpha` | `string` | Yes | The hotkey's **own** alpha on this subnet (alpha-RAO decimal string) — `TotalHotkeyAlpha(hotkey, netuid)`. Not the same quantity as `alpha_stake` above, which is the chain's *inherited* stake (own − delegated to children + inherited from parents) after the family-parity adjustment. Collateral is locked inside this pool, so this is the base the split below is taken from: `locked_alpha + free_alpha == hotkey_alpha` (see `locked_alpha` for the one flooring exception). `"0"` on rows written before these columns existed — the value was never recorded there, and `"0"` on both base and free is what says so. | | `data[].in_danger` | `boolean` | Yes | Whether the neuron is in danger of deregistration. | | `data[].incentive` | `string` | Yes | Incentive: recall's split-weighted incentive across the subnet's mechanisms, normalised to `0..=1` (#1068); the plain `u16 / 65535` on a single-mechanism subnet. | | `data[].is_child_key` | `boolean` | Yes | Whether this hotkey is a child key on this subnet. | | `data[].is_immune` | `boolean` | Yes | Whether the neuron is within its immunity period. | | `data[].is_owner_hotkey` | `boolean, nullable` | Yes | Whether this is the subnet owner's hotkey. Derived from the snapshot's projected `daily_owner_alpha`, which the indexer sets **iff** the neuron's hotkey owns the subnet. `null` on rows written before the projected fields landed (#345), where owner state is genuinely unknown rather than false. | | `data[].locked_alpha` | `string` | Yes | Alpha still locked as v435 registration collateral (alpha-RAO decimal string). **Part of** `hotkey_alpha`, not additional to it — the lock is a flag on real stake. One chain-side exception: both figures floor independently through the share pool, so a long-bonded position can carry a lock a few rao above its own pool, and `free_alpha` floors at `"0"` there rather than the sum holding exactly. `"0"` for every neuron whose hotkey holds no collateral. Registration locks it only on a subnet with a nonzero `CollateralLockShare`, but a voluntary `add_collateral` can lock it on any subnet. | | `data[].mech_incentive` | `array` | Yes | Per-mechanism incentive (normalised `u16 / 65535`, one per mechanism). | | `data[].mech_incentive[]` | `string` | Yes | | | `data[].mech_updated` | `array` | Yes | Blocks since each mechanism's last update (one element per mechanism). | | `data[].mech_updated[]` | `integer (int32)` | Yes | | | `data[].min_locked_alpha` | `string` | Yes | The miner-set collateral floor (alpha-RAO decimal string). The drain never releases `locked_alpha` below it, and below it the chain captures earned incentive into the lock instead of paying it out. `"0"` when no floor is set. | | `data[].miner_rank` | `integer (int32), nullable` | Yes | Miner rank among positive-incentive neurons (`null` otherwise). Equal incentives share a rank and the next value skips (1, 1, 3), matching recall's `rankBy`. | | `data[].name` | `string, nullable` | Yes | Identity name (from the coldkey's on-chain identity). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].registered_at_block` | `integer (int32)` | Yes | Block at which the neuron registered. | | `data[].root_stake` | `string` | Yes | Root stake (RAO, `u64` decimal string): the snapshot row's `tao_stake`. | | `data[].root_stake_as_alpha` | `string` | Yes | Root stake as alpha (RAO, `u64` decimal string): `total_alpha_stake - alpha_stake`. | | `data[].root_weight` | `string` | Yes | Root weight (decimal string; family-derived, `"0"` if no family row). | | `data[].stake_weight` | `string, nullable` | Yes | Stake weight (normalised `u16 / 65535`). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].total_alpha_stake` | `string` | Yes | Total alpha stake (RAO, `u64` decimal string): the snapshot row's `total_stake`. | | `data[].uid` | `integer (int32)` | Yes | Neuron UID. | | `data[].updated` | `integer (int32)` | Yes | Blocks since the neuron's last update (`block_number - last_update`). | | `data[].validator_permit` | `boolean` | Yes | Whether the neuron holds a validator permit. | | `data[].validator_rank` | `integer (int32), nullable` | Yes | Validator rank (among positive-dividend neurons; `null` otherwise). | | `data[].validator_trust` | `string` | Yes | Validator trust (normalised `u16 / 65535`). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Metagraph Latest per-neuron snapshot. _Source: https://taostats.io/docs/new/subnets/get-subnets-metagraph_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/metagraph ``` Requires an API key in the `Authorization` header. Latest per-neuron snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/metagraph" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/metagraph', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/metagraph", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `uid` | query | `integer (int32)` | | Filter by neuron UID. | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `active` | query | `boolean` | | Filter by active flag. | | `validator_permit` | query | `boolean` | | Filter by validator-permit flag. | | `is_immune` | query | `boolean` | | Filter by immunity flag (derived). | | `in_danger` | query | `boolean` | | Filter by danger flag (derived). | | `is_child_key` | query | `boolean` | | Filter by child-key flag (derived). | | `has_dividends` | query | `boolean` | | Keep only neurons with positive dividends. | | `has_incentive` | query | `boolean` | | Keep only neurons with positive incentive. | | `search` | query | `string` | | Free-text search over uid / hotkey / coldkey / axon IP. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 256, so one subnet's neurons (at most 256) fit in a single page. Every other route, including `/v1/subnets/metagraph/history`, keeps a maximum of 200. | | `order_by` | query | `string` | | Column to order by. Default: `total_alpha_stake`. One of `total_alpha_stake`, `netuid`, `uid`, `emission`, `incentive`, `dividends`, `consensus`, `validator_trust`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Latest per-neuron metagraph snapshot | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].active` | `boolean` | Yes | Whether the neuron is active. | | `data[].alpha_stake` | `string` | Yes | Alpha stake (RAO, `u64` decimal string): the chain's inherited alpha stake at the row's block, the snapshot row's `alpha_stake`. | | `data[].axon` | `string, nullable` | Yes | Axon `ip:port` (`null` when no axon is set). | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].collateral_earned_alpha` | `string` | Yes | Emission earned while this collateral entry has existed (alpha-RAO decimal string) — the progress meter against the lock, since release is `drain_ratio × emission`. Not a lifetime-of-hotkey total. | | `data[].consensus` | `string` | Yes | Consensus score (normalised `u16 / 65535`). | | `data[].daily_burned_alpha` | `string, nullable` | Yes | Daily burned alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_burned_alpha_as_tao` | `string, nullable` | Yes | Daily burned alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_alpha` | `string, nullable` | Yes | Daily mining alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_alpha_as_tao` | `string, nullable` | Yes | Daily mining alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_mining_tao` | `string, nullable` | Yes | Daily mining TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_owner_alpha` | `string, nullable` | Yes | Daily owner alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_owner_alpha_as_tao` | `string, nullable` | Yes | Daily owner alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_total_rewards_as_tao` | `string, nullable` | Yes | Daily total rewards as TAO (RAO decimal string; always present). | | `data[].daily_validating_alpha` | `string, nullable` | Yes | Daily validating alpha (RAO decimal string; `null` when not applicable). | | `data[].daily_validating_alpha_as_tao` | `string, nullable` | Yes | Daily validating alpha as TAO (RAO decimal string; `null` when not applicable). | | `data[].daily_validating_tao` | `string, nullable` | Yes | Daily validating TAO (RAO decimal string; `null` when not applicable). | | `data[].dividends` | `string` | Yes | Dividends (normalised `u16 / 65535`). | | `data[].emission` | `string` | Yes | Emission (RAO, `u64` decimal string). | | `data[].free_alpha` | `string` | Yes | The hotkey's own alpha not frozen by collateral: `hotkey_alpha - locked_alpha`, floored at `"0"` (alpha-RAO decimal string). Read this, not `alpha_stake`, as "withdrawable at this UID". Per-*position* (coldkey, hotkey, netuid) withdrawable lives on `/v1/alpha/*`. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].hotkey_alpha` | `string` | Yes | The hotkey's **own** alpha on this subnet (alpha-RAO decimal string) — `TotalHotkeyAlpha(hotkey, netuid)`. Not the same quantity as `alpha_stake` above, which is the chain's *inherited* stake (own − delegated to children + inherited from parents) after the family-parity adjustment. Collateral is locked inside this pool, so this is the base the split below is taken from: `locked_alpha + free_alpha == hotkey_alpha` (see `locked_alpha` for the one flooring exception). `"0"` on rows written before these columns existed — the value was never recorded there, and `"0"` on both base and free is what says so. | | `data[].in_danger` | `boolean` | Yes | Whether the neuron is in danger of deregistration. | | `data[].incentive` | `string` | Yes | Incentive: recall's split-weighted incentive across the subnet's mechanisms, normalised to `0..=1` (#1068); the plain `u16 / 65535` on a single-mechanism subnet. | | `data[].is_child_key` | `boolean` | Yes | Whether this hotkey is a child key on this subnet. | | `data[].is_immune` | `boolean` | Yes | Whether the neuron is within its immunity period. | | `data[].is_owner_hotkey` | `boolean, nullable` | Yes | Whether this is the subnet owner's hotkey. Derived from the snapshot's projected `daily_owner_alpha`, which the indexer sets **iff** the neuron's hotkey owns the subnet. `null` on rows written before the projected fields landed (#345), where owner state is genuinely unknown rather than false. | | `data[].locked_alpha` | `string` | Yes | Alpha still locked as v435 registration collateral (alpha-RAO decimal string). **Part of** `hotkey_alpha`, not additional to it — the lock is a flag on real stake. One chain-side exception: both figures floor independently through the share pool, so a long-bonded position can carry a lock a few rao above its own pool, and `free_alpha` floors at `"0"` there rather than the sum holding exactly. `"0"` for every neuron whose hotkey holds no collateral. Registration locks it only on a subnet with a nonzero `CollateralLockShare`, but a voluntary `add_collateral` can lock it on any subnet. | | `data[].mech_incentive` | `array` | Yes | Per-mechanism incentive (normalised `u16 / 65535`, one per mechanism). | | `data[].mech_incentive[]` | `string` | Yes | | | `data[].mech_updated` | `array` | Yes | Blocks since each mechanism's last update (one element per mechanism). | | `data[].mech_updated[]` | `integer (int32)` | Yes | | | `data[].min_locked_alpha` | `string` | Yes | The miner-set collateral floor (alpha-RAO decimal string). The drain never releases `locked_alpha` below it, and below it the chain captures earned incentive into the lock instead of paying it out. `"0"` when no floor is set. | | `data[].miner_rank` | `integer (int32), nullable` | Yes | Miner rank among positive-incentive neurons (`null` otherwise). Equal incentives share a rank and the next value skips (1, 1, 3), matching recall's `rankBy`. | | `data[].name` | `string, nullable` | Yes | Identity name (from the coldkey's on-chain identity). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].registered_at_block` | `integer (int32)` | Yes | Block at which the neuron registered. | | `data[].root_stake` | `string` | Yes | Root stake (RAO, `u64` decimal string): the snapshot row's `tao_stake`. | | `data[].root_stake_as_alpha` | `string` | Yes | Root stake as alpha (RAO, `u64` decimal string): `total_alpha_stake - alpha_stake`. | | `data[].root_weight` | `string` | Yes | Root weight (decimal string; family-derived, `"0"` if no family row). | | `data[].stake_weight` | `string, nullable` | Yes | Stake weight (normalised `u16 / 65535`). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].total_alpha_stake` | `string` | Yes | Total alpha stake (RAO, `u64` decimal string): the snapshot row's `total_stake`. | | `data[].uid` | `integer (int32)` | Yes | Neuron UID. | | `data[].updated` | `integer (int32)` | Yes | Blocks since the neuron's last update (`block_number - last_update`). | | `data[].validator_permit` | `boolean` | Yes | Whether the neuron holds a validator permit. | | `data[].validator_rank` | `integer (int32), nullable` | Yes | Validator rank (among positive-dividend neurons; `null` otherwise). | | `data[].validator_trust` | `string` | Yes | Validator trust (normalised `u16 / 65535`). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Metagraph Aggregate History Aggregated snapshots over time. _Source: https://taostats.io/docs/new/subnets/get-subnets-metagraph-aggregate-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/metagraph/aggregate/history ``` Requires an API key in the `Authorization` header. Aggregated snapshots over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/metagraph/aggregate/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/metagraph/aggregate/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/metagraph/aggregate/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Aggregated metagraph snapshots over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].immune_count` | `integer (int32)` | Yes | Number of neurons within their immunity period. | | `data[].last_dereg_emission` | `string` | Yes | Emission of the most recent deregistration before this block (pass-through string; `"0"` if none). | | `data[].last_dereg_incentive` | `string` | Yes | Incentive of the most recent deregistration before this block (pass-through string; `"0"` if none). | | `data[].max_danger_emission` | `string` | Yes | Max emission among the danger-set neurons (RAO decimal string). | | `data[].max_danger_incentive` | `string` | Yes | Max incentive among the danger-set neurons (split-weighted, normalised). | | `data[].max_emission` | `string` | Yes | Max emission across all neurons (RAO decimal string). | | `data[].max_immune_emission` | `string` | Yes | Max emission among immune neurons (RAO, u64 decimal string). | | `data[].max_immune_incentive` | `string` | Yes | Max incentive among immune neurons: recall's split-weighted incentive, normalised to `0..=1` (the plain `u16 / 65535` on a single-mechanism subnet). | | `data[].max_incentive` | `string` | Yes | Max incentive across all neurons (split-weighted, normalised). | | `data[].max_mining_emission` | `string` | Yes | Max emission among neurons with positive incentive (RAO decimal). | | `data[].min_non_immune_emission` | `string` | Yes | Min emission among non-immune positive-incentive neurons (RAO decimal). | | `data[].min_non_immune_incentive` | `string` | Yes | Min positive incentive among non-immune neurons (split-weighted, normalised). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Metagraph Aggregate Latest aggregated snapshot per subnet. _Source: https://taostats.io/docs/new/subnets/get-subnets-metagraph-aggregate_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/metagraph/aggregate ``` Requires an API key in the `Authorization` header. Latest aggregated snapshot per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/metagraph/aggregate" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/metagraph/aggregate', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/metagraph/aggregate", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Latest aggregated metagraph snapshot per subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].immune_count` | `integer (int32)` | Yes | Number of neurons within their immunity period. | | `data[].last_dereg_emission` | `string` | Yes | Emission of the most recent deregistration before this block (pass-through string; `"0"` if none). | | `data[].last_dereg_incentive` | `string` | Yes | Incentive of the most recent deregistration before this block (pass-through string; `"0"` if none). | | `data[].max_danger_emission` | `string` | Yes | Max emission among the danger-set neurons (RAO decimal string). | | `data[].max_danger_incentive` | `string` | Yes | Max incentive among the danger-set neurons (split-weighted, normalised). | | `data[].max_emission` | `string` | Yes | Max emission across all neurons (RAO decimal string). | | `data[].max_immune_emission` | `string` | Yes | Max emission among immune neurons (RAO, u64 decimal string). | | `data[].max_immune_incentive` | `string` | Yes | Max incentive among immune neurons: recall's split-weighted incentive, normalised to `0..=1` (the plain `u16 / 65535` on a single-mechanism subnet). | | `data[].max_incentive` | `string` | Yes | Max incentive across all neurons (split-weighted, normalised). | | `data[].max_mining_emission` | `string` | Yes | Max emission among neurons with positive incentive (RAO decimal). | | `data[].min_non_immune_emission` | `string` | Yes | Min emission among non-immune positive-incentive neurons (RAO decimal). | | `data[].min_non_immune_incentive` | `string` | Yes | Min positive incentive among non-immune neurons (split-weighted, normalised). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Metrics Per-subnet operational metrics. _Source: https://taostats.io/docs/new/subnets/get-subnets-metrics_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/metrics ``` Requires an API key in the `Authorization` header. Per-subnet operational metrics. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/metrics" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/metrics', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/metrics", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of per-subnet metrics | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].active_dual` | `integer (int32)` | Yes | Number of active dual (miner+validator) keys. | | `data[].active_keys` | `integer (int32)` | Yes | Number of active keys (registered neurons). | | `data[].active_miners` | `integer (int32)` | Yes | Number of active miners (non-zero incentive). | | `data[].active_validators` | `integer (int32)` | Yes | Number of active validators (non-zero dividends). | | `data[].block_number` | `integer (int32)` | Yes | Block the snapshot was taken at. | | `data[].blocks_since_last_epoch` | `integer (int64)` | Yes | Blocks since the last epoch. | | `data[].blocks_until_next_adjustment` | `integer (int64)` | Yes | Blocks remaining until the next adjustment (may be negative if overdue). | | `data[].blocks_until_next_epoch` | `integer (int64)` | Yes | Blocks remaining until the next epoch. | | `data[].emission` | `string, nullable` | Yes | `SubnetTaoInEmission` at `block_number`: TAO injected into the subnet's pool on that block (RAO, u64 as string), the same column and name as `/v1/subnets/history`'s `emission`. `null` only on a row written before #1285. | | `data[].excess_tao` | `string, nullable` | Yes | `SubnetExcessTao` at `block_number`: TAO the chain bought into the subnet (RAO, u64 as string). `null` only on a row written before #1285. | | `data[].incentive_burn` | `string` | Yes | Owner-neuron incentive sum (normalised fraction as string). | | `data[].last_adjustment_block` | `integer (int64), nullable` | Yes | Block of the last difficulty/burn adjustment, or `null`. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].neuron_registration_cost` | `string` | Yes | Current neuron registration cost (RAO, u64 as string). | | `data[].neuron_registrations_this_interval` | `integer (int32)` | Yes | Registrations in the current adjustment interval. | | `data[].owner` | `string, nullable` | Yes | Owner coldkey (SS58), or `null` when absent. | | `data[].owner_hotkey` | `string, nullable` | Yes | Owner hotkey (SS58), or `null` when absent. | | `data[].recycled_lifetime` | `string, nullable` | Yes | `RAORecycledForRegistration` at `block_number`: TAO recycled by neuron registrations on this subnet (RAO, u64 as string). `null` only on a row written before #1285. Root serves the OLD API's frozen root total instead (see the module docs). | | `data[].recycled_since_registration` | `string, nullable` | Yes | `recycled_lifetime` minus the counter's value at the subnet's latest registration at or before `block_number` (RAO, u64 as string). `null` when `recycled_lifetime` is. Root serves the OLD API's frozen root total, the same as its `recycled_lifetime`. | | `data[].registration_block_number` | `integer (int32), nullable` | Yes | Block the subnet was registered at, or `null` when unknown. | | `data[].registration_cost` | `string, nullable` | Yes | Subnet registration cost (RAO, u64 as string), or `null` when absent. | | `data[].registration_timestamp` | `string, nullable` | Yes | Subnet registration time (ISO 8601), or `null` when unknown. | | `data[].tao_from_neuron_registration_24_hours` | `string` | Yes | TAO from neuron registrations in the last 24h (RAO, u64 as string). `"0"` for root, as the OLD API serves it. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].validators` | `integer (int32)` | Yes | Total validators (equals `active_validators`; old API parity). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Neuron Deregistration Events GET /v1/subnets/neuron-deregistration-events — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-neuron-deregistration-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/neuron-deregistration-events ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/neuron-deregistration-events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/neuron-deregistration-events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/neuron-deregistration-events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `uid` | query | `integer (int32)` | | Filter by neuron UID. | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of neuron deregistration events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].emission` | `string` | Yes | Emission at time of deregistration (decimal string). | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].incentive` | `string` | Yes | Incentive at time of deregistration (decimal string). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].uid` | `integer (int32)` | Yes | Neuron UID. | | `data[].was_drained` | `boolean` | Yes | Whether the neuron was drained before deregistration. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Neuron Registration Events GET /v1/subnets/neuron-registration-events — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-neuron-registration-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/neuron-registration-events ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/neuron-registration-events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/neuron-registration-events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/neuron-registration-events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `uid` | query | `integer (int32)` | | Filter by neuron UID. | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of neuron registration events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].registration_cost` | `string` | Yes | Registration cost (RAO, u64 as decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].uid` | `integer (int32)` | Yes | Neuron UID. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Owners Returns one entry per netuid each time its owner coldkey changes, with the pending_owner* fields filled from the chain's announced coldkey swaps where the… _Source: https://taostats.io/docs/new/subnets/get-subnets-owners_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/owners ``` Requires an API key in the `Authorization` header. Returns one entry per netuid each time its owner coldkey changes, with the `pending_owner*` fields filled from the chain's announced coldkey swaps where the row's owner has one (#840). ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/owners" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/owners', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/owners", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `owner` | query | `string` | | Filter by owner coldkey (SS58 or 0x-hex). | | `is_coldkey_swap` | query | `boolean` | | Filter by coldkey-swap flag. | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Subnet ownership change history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].is_coldkey_swap` | `boolean` | Yes | Whether this change was caused by a coldkey swap. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].owner` | `string` | Yes | New owner coldkey (SS58). | | `data[].pending_owner` | `string, nullable` | Yes | The coldkey this row's owner is swapping to (SS58), or `null` — which is the normal case even where a swap is scheduled, because an announcement commits only a hash of the incoming coldkey until it is revealed. | | `data[].pending_owner_block_number` | `integer (int32), nullable` | Yes | Block at which this row's owner coldkey can execute its announced swap, or `null` when it has not announced one. On the subnet's current-owner row that is the pending handover; on an older row it describes that coldkey only, not the subnet's next change of hands. | | `data[].pending_owner_timestamp` | `string, nullable` | Yes | ISO 8601 time of `pending_owner_block_number` — its real block time once that block exists, and until then projected from the head. `null` when this row's owner has not announced a swap. | | `data[].previous_owner` | `string, nullable` | Yes | Prior owner coldkey (SS58), or `null` if previous owner was root/absent. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Pools History Pool snapshots over time for one subnet. _Source: https://taostats.io/docs/new/subnets/get-subnets-pools-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/pools/history ``` Requires an API key in the `Authorization` header. Pool snapshots over time for one subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/pools/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/pools/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/pools/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | Yes | Subnet ID (required). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `frequency` | query | `string` | | How far apart the points are: `by_block`, `by_hour` or `by_day`. Default: `by_day`, as on the OLD API. Buckets are block-number multiples here — every 7,200th block for a day, every 300th for an hour — which is the rule the OLD API uses on this endpoint. Values are sampled at that block; there is nothing to aggregate, because this endpoint serves state and carries no volume field. One of `by_block`, `by_hour`, `by_day`. | ## Responses ### `200` — Pool snapshots over time for one subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_in_pool` | `string` | Yes | Alpha in pool (RAO, u64 as decimal string). | | `data[].alpha_staked` | `string` | Yes | Alpha staked (RAO, u64 as decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].liquidity` | `string` | Yes | Liquidity (decimal string). | | `data[].market_cap` | `string` | Yes | Market cap (decimal string). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price` | `string` | Yes | Current price (decimal string). | | `data[].root_prop` | `string` | Yes | Root proportion (decimal string). | | `data[].startup_mode` | `boolean` | Yes | Whether the subnet is in startup mode. | | `data[].subnet_emission_enabled` | `boolean, nullable` | Yes | Whether the chain is running this subnet's pool-side emission (`SubtensorModule::SubnetEmissionEnabled`). `false` means the chain treats the subnet's `alpha_in`, `tao_in` and `excess_tao` chain buys as zero. `null` means **this block's row does not record it**, not "enabled", and it means that for either of two reasons. The block is below spec 411 (8,283,784), where the chain has no such switch at all and so there is nothing to record. Or the row was written before this column existed and the backfill has not reached it — and the backfill only covers spec 411 upward, because below that there is no value to fill in. From spec 411 up, a row this indexer writes always carries a value: an absent key is the storage item's own `true` default. **This field couples the API's rollout to the indexer's.** These routes select `?fields` expanded from `SubnetPoolRow`, so this binary names this column on every query, and the column only exists once `subnet-pools-v1`'s `ensure_tables` has added it. Roll the indexer first; an API rolled ahead of it returns 500 here until the indexer catches up. See `docs/deployment.md`, "A column added to an indexer's row". Carried on `/v1/subnets/pools` and `/v1/subnets/pools/history` alike, because it is a column of `subnet_pool_v1` rather than a latest-only enrichment like `subnet_protocol_alpha`. | | `data[].subnet_protocol_alpha` | `string, nullable` | Yes | `SubtensorModule::SubnetProtocolAlpha(netuid)` — the alpha the coinbase bought back with this subnet's excess TAO and holds on the protocol's behalf (RAO, u64 as decimal string). `null` means **unknown**, never zero: from spec 413 the storage item is `ValueQuery`, so a subnet the chain has never written reads a real `0`. Only `/v1/subnets/pools` carries it — the value is indexed on `subnet_pool_latest_v1` alone, so `/v1/subnets/pools/history` always serves `null`. | | `data[].symbol` | `string` | Yes | Subnet token symbol. | | `data[].tao_in_pool` | `string` | Yes | TAO reserves in the subnet's pool (RAO, u64 as decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].total_alpha` | `string` | Yes | Total alpha in subnet (RAO, u64 as decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Pools Latest pool snapshot per subnet. _Source: https://taostats.io/docs/new/subnets/get-subnets-pools_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/pools ``` Requires an API key in the `Authorization` header. Latest pool snapshot per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/pools" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/pools', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/pools", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`, `price`, `liquidity`, `market_cap`, `tao_in_pool`, `total_alpha`, `alpha_in_pool`, `alpha_staked`, `root_prop`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Latest pool snapshot per subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_in_pool` | `string` | Yes | Alpha in pool (RAO, u64 as decimal string). | | `data[].alpha_staked` | `string` | Yes | Alpha staked (RAO, u64 as decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].liquidity` | `string` | Yes | Liquidity (decimal string). | | `data[].market_cap` | `string` | Yes | Market cap (decimal string). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price` | `string` | Yes | Current price (decimal string). | | `data[].root_prop` | `string` | Yes | Root proportion (decimal string). | | `data[].startup_mode` | `boolean` | Yes | Whether the subnet is in startup mode. | | `data[].subnet_emission_enabled` | `boolean, nullable` | Yes | Whether the chain is running this subnet's pool-side emission (`SubtensorModule::SubnetEmissionEnabled`). `false` means the chain treats the subnet's `alpha_in`, `tao_in` and `excess_tao` chain buys as zero. `null` means **this block's row does not record it**, not "enabled", and it means that for either of two reasons. The block is below spec 411 (8,283,784), where the chain has no such switch at all and so there is nothing to record. Or the row was written before this column existed and the backfill has not reached it — and the backfill only covers spec 411 upward, because below that there is no value to fill in. From spec 411 up, a row this indexer writes always carries a value: an absent key is the storage item's own `true` default. **This field couples the API's rollout to the indexer's.** These routes select `?fields` expanded from `SubnetPoolRow`, so this binary names this column on every query, and the column only exists once `subnet-pools-v1`'s `ensure_tables` has added it. Roll the indexer first; an API rolled ahead of it returns 500 here until the indexer catches up. See `docs/deployment.md`, "A column added to an indexer's row". Carried on `/v1/subnets/pools` and `/v1/subnets/pools/history` alike, because it is a column of `subnet_pool_v1` rather than a latest-only enrichment like `subnet_protocol_alpha`. | | `data[].subnet_protocol_alpha` | `string, nullable` | Yes | `SubtensorModule::SubnetProtocolAlpha(netuid)` — the alpha the coinbase bought back with this subnet's excess TAO and holds on the protocol's behalf (RAO, u64 as decimal string). `null` means **unknown**, never zero: from spec 413 the storage item is `ValueQuery`, so a subnet the chain has never written reads a real `0`. Only `/v1/subnets/pools` carries it — the value is indexed on `subnet_pool_latest_v1` alone, so `/v1/subnets/pools/history` always serves `null`. | | `data[].symbol` | `string` | Yes | Subnet token symbol. | | `data[].tao_in_pool` | `string` | Yes | TAO reserves in the subnet's pool (RAO, u64 as decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].total_alpha` | `string` | Yes | Total alpha in subnet (RAO, u64 as decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Pools Aggregate Per-subnet rolling market aggregates. _Source: https://taostats.io/docs/new/subnets/get-subnets-pools-aggregate_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/pools/aggregate ``` Requires an API key in the `Authorization` header. Per-subnet rolling market aggregates. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/pools/aggregate" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/pools/aggregate', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/pools/aggregate", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`, `market_cap_change_1_day`, `price_change_1_hour`, `price_change_1_day`, `price_change_1_week`, `price_change_1_month`, `tao_volume_24_hr`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Per-subnet pool aggregates | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_buy_volume_24_hr` | `string` | Yes | Alpha buy volume in the last 24h (RAO, u64 as decimal string). | | `data[].alpha_sell_volume_24_hr` | `string` | Yes | Alpha sell volume in the last 24h (RAO, u64 as decimal string). | | `data[].alpha_volume_24_hr` | `string` | Yes | Alpha volume in the last 24h (RAO, u64 as decimal string). | | `data[].alpha_volume_24_hr_change_1_day` | `string, nullable` | Yes | Alpha 24h-volume change vs the prior 24h, percent (decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Anchor block height (latest snapshot block). | | `data[].buyers_24_hr` | `integer (int32)` | Yes | Number of unique buyers in the last 24h. | | `data[].buys_24_hr` | `integer (int32)` | Yes | Number of buys in the last 24h. | | `data[].highest_price_24_hr` | `string, nullable` | Yes | Highest price in the 24h window (decimal string). | | `data[].last_price` | `string, nullable` | Yes | Last traded price in the 24h window (decimal string). | | `data[].lowest_price_24_hr` | `string, nullable` | Yes | Lowest price in the 24h window (decimal string). | | `data[].market_cap_change_1_day` | `string, nullable` | Yes | Market-cap change over 1 day, percent (decimal string). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].price_change_1_day` | `string, nullable` | Yes | Price change over 1 day, percent (decimal string). | | `data[].price_change_1_hour` | `string, nullable` | Yes | Price change over 1 hour, percent (decimal string). | | `data[].price_change_1_month` | `string, nullable` | Yes | Price change over 1 month, percent (decimal string). | | `data[].price_change_1_week` | `string, nullable` | Yes | Price change over 1 week, percent (decimal string). | | `data[].sellers_24_hr` | `integer (int32)` | Yes | Number of unique voluntary sellers in the last 24h, excluding liquidation recipients. | | `data[].sells_24_hr` | `integer (int32)` | Yes | Number of voluntary sells in the last 24h, excluding forced liquidation payouts. | | `data[].sentiment` | `string, nullable` | Yes | Sentiment label derived from `sentiment_index`: one of `Extreme Fear`, `Fear`, `Neutral`, `Greed`, `Extreme Greed` (recall's enum strings, matching the old API and `/v1/subnets/pools/total-price` — issue #420). `null` whenever `sentiment_index` is. | | `data[].sentiment_index` | `string, nullable` | Yes | Subnet Sentiment Index — the 7-component weighted SSI as a 1-dp decimal string in `[0, 100]` (#169). `null` for root (netuid 0), startup-mode subnets, and subnets with no pool snapshot at the anchor block. | | `data[].seven_day_prices` | `array` | Yes | Sampled price snapshots over the last seven days, ascending (~4h grid plus the 1h-ago and anchor blocks — not strictly one-per-day). | | `data[].seven_day_prices[].block_number` | `integer (int32)` | Yes | Block height of the sample. | | `data[].seven_day_prices[].price` | `string` | Yes | Price at the sample block (decimal string). | | `data[].seven_day_prices[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].tao_buy_volume_24_hr` | `string` | Yes | TAO buy volume in the last 24h (RAO, u64 as decimal string). | | `data[].tao_sell_volume_24_hr` | `string` | Yes | TAO sell volume in the last 24h (RAO, u64 as decimal string). | | `data[].tao_volume_24_hr` | `string` | Yes | TAO volume in the last 24h (RAO, u64 as decimal string). | | `data[].tao_volume_24_hr_change_1_day` | `string, nullable` | Yes | TAO 24h-volume change vs the prior 24h, percent (decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp of the anchor block. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Pools Total Price History GET /v1/subnets/pools/total-price/history — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-pools-total-price-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/pools/total-price/history ``` Requires an API key in the `Authorization` header. Aggregate over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/pools/total-price/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/pools/total-price/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/pools/total-price/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `frequency` | query | `string` | | How far apart the points are: `by_block`, `by_hour` or `by_day`. Default: `by_day`, as on the OLD API. At `by_hour` and `by_day` the six volume fields are **summed** over the bucket, and `block_number`, `timestamp` and `price` come from the bucket's last block. Sampling one block's volume instead would under-report a day by about the number of blocks in it. One of `by_block`, `by_hour`, `by_day`. | ## Responses ### `200` — Network-wide aggregate pool price over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_buy_volume` | `string` | Yes | netuid\>0 stake volume this block, RAO TAO (decimal string). | | `data[].alpha_sell_volume` | `string` | Yes | netuid\>0 unstake volume this block, RAO TAO (decimal string). | | `data[].alpha_volume` | `string` | Yes | Total alpha-pool (netuid\>0) delegation volume this block, RAO TAO (decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].price` | `string` | Yes | Sum of per-subnet price across non-root, non-startup pools (decimal string). | | `data[].root_buy_volume` | `string` | Yes | netuid=0 stake volume this block, RAO TAO (decimal string). | | `data[].root_sell_volume` | `string` | Yes | netuid=0 unstake volume this block, RAO TAO (decimal string). | | `data[].root_volume` | `string` | Yes | Total root (netuid=0) delegation volume this block, RAO TAO (decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Pools Total Price Latest network-wide aggregate. _Source: https://taostats.io/docs/new/subnets/get-subnets-pools-total-price_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/pools/total-price ``` Requires an API key in the `Authorization` header. Latest network-wide aggregate. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/pools/total-price" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/pools/total-price', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/pools/total-price", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest network-wide aggregate pool price | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `object` | Yes | The latest network-wide total-price snapshot. Includes the sentiment fields, which only the newest block reports. | | `data.alpha_buy_volume` | `string` | Yes | netuid\>0 stake volume this block, RAO TAO (decimal string). | | `data.alpha_sell_volume` | `string` | Yes | netuid\>0 unstake volume this block, RAO TAO (decimal string). | | `data.alpha_volume` | `string` | Yes | Total alpha-pool (netuid\>0) delegation volume this block, RAO TAO (decimal string). | | `data.block_number` | `integer (int32)` | Yes | Block height. | | `data.price` | `string` | Yes | Sum of per-subnet price across non-root, non-startup pools (decimal string). | | `data.root_buy_volume` | `string` | Yes | netuid=0 stake volume this block, RAO TAO (decimal string). | | `data.root_sell_volume` | `string` | Yes | netuid=0 unstake volume this block, RAO TAO (decimal string). | | `data.root_volume` | `string` | Yes | Total root (netuid=0) delegation volume this block, RAO TAO (decimal string). | | `data.sentiment` | `string` | Yes | Sentiment label derived from `sentiment_index`. | | `data.sentiment_index` | `string` | Yes | Sentiment index 0–100 (decimal string) — a two-week price rate-of-change mapped onto [0,100]. | | `data.timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | ### `404` — No total-price snapshot recorded yet | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Registration Cost History Per-block time series. _Source: https://taostats.io/docs/new/subnets/get-subnets-registration-cost-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/registration-cost/history ``` Requires an API key in the `Authorization` header. Per-block time series. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/registration-cost/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/registration-cost/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/registration-cost/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_number` | query | `integer (int32)` | | Exact block number filter. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Subnet registration cost over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].is_purchase` | `boolean` | Yes | Whether a subnet was bought at this block's price — true when the *next* block registered a subnet, which is the block whose cost the buyer paid. Worked out per request from `subnet_registration_v1` (#846); the stored column of this name is never written and is not read here. | | `data[].registration_cost` | `string` | Yes | Subnet registration lock cost in RAO (u64 rendered as a string), matching the old API and `/v1/subnets/registrations`. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Registration Cost GET /v1/subnets/registration-cost — Taostats API endpoint in the Subnets group. _Source: https://taostats.io/docs/new/subnets/get-subnets-registration-cost_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/registration-cost ``` Requires an API key in the `Authorization` header. Latest snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/registration-cost" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/registration-cost', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/registration-cost", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — Latest subnet registration cost | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `object` | Yes | One row of subnet registration cost data. | | `data.block_number` | `integer (int32)` | Yes | Block height. | | `data.is_purchase` | `boolean` | Yes | Whether a subnet was bought at this block's price — true when the *next* block registered a subnet, which is the block whose cost the buyer paid. Worked out per request from `subnet_registration_v1` (#846); the stored column of this name is never written and is not read here. | | `data.registration_cost` | `string` | Yes | Subnet registration lock cost in RAO (u64 rendered as a string), matching the old API and `/v1/subnets/registrations`. | | `data.timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | ### `404` — No data yet | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Registrations Subnet registration events. _Source: https://taostats.io/docs/new/subnets/get-subnets-registrations_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/registrations ``` Requires an API key in the `Authorization` header. Subnet registration events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/registrations" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/registrations', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/registrations", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `owner` | query | `string` | | Filter by owner coldkey (SS58 or 0x-hex). | | `registered_by` | query | `string` | | Filter by registering coldkey (SS58 or 0x-hex). | | `block_number` | query | `integer (int32)` | | Filter by exact block number. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of subnet registration events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].owner` | `string, nullable` | Yes | Subnet owner coldkey (SS58), or `null` when storage was absent. | | `data[].registered_by` | `string, nullable` | Yes | Extrinsic signer coldkey (SS58), or `null` when unavailable. | | `data[].registration_cost` | `string` | Yes | Registration lock cost the buyer paid (RAO, u64 as decimal string). On-chain `SubnetLocked`, or `NetworkLastLockCost` for registrations from dTAO launch to spec 320 — may differ from old API for post-dTAO subnets due to a known recall override bug that is not replicated. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Stake Events DTAO-era alpha stake/unstake events. _Source: https://taostats.io/docs/new/subnets/get-subnets-stake-events_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/stake-events ``` Requires an API key in the `Authorization` header. DTAO-era alpha stake/unstake events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/stake-events" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/stake-events', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/stake-events", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `action` | query | `string` | | Filter by action. Default: `all`. One of `stake`, `unstake`, `all`. | | `is_transfer` | query | `boolean` | | Filter by transfer flag. | | `transfer_address` | query | `string` | | Filter by transfer counterparty address (SS58 or 0x-hex). | | `trades_only` | query | `boolean` | | `true` keeps only the rows the indexer's `TRADE_ONLY_PREDICATE` counts as trades, the same definition the pool volume figures and the CoinGecko and CoinMarketCap feeds use: it drops stake transfers, hotkey-swap legs and, from block 6,067,944, within-subnet move legs. Where that definition is wrong (#1403), this follows it. `false` or absent applies no filter. | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `extrinsic_id` | query | `string` | | Filter by extrinsic ID. | | `amount_min` | query | `integer (int64)` | | Minimum TAO amount (RAO, inclusive). | | `amount_max` | query | `integer (int64)` | | Maximum TAO amount (RAO, inclusive). | | `block_number` | query | `integer (int32)` | | Filter by a specific block. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of dTAO stake/unstake events | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].action` | `string` | Yes | `stake` or `unstake`. | | `data[].alpha` | `string, nullable` | Yes | Alpha amount (RAO, u64 as decimal string). | | `data[].alpha_price_in_tao` | `string, nullable` | Yes | The trade's average execution price in TAO — its own TAO leg divided by its own alpha leg, not the subnet pool's spot price. See `docs/api_spec.md`, "An event's price is an execution price" (#984). | | `data[].alpha_price_in_usd` | `string, nullable` | Yes | The execution price above, in USD at the price in force at the event, to two decimal places. `null` when no price covers the row. Two decimal places is savage on a cheap alpha — anything under half a cent reads as `"0.00"` — but that is what the OLD API serves and parity is the point. | | `data[].amount` | `string` | Yes | TAO amount (RAO, u64 as decimal string). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].extrinsic_id` | `string, nullable` | Yes | Extrinsic ID. | | `data[].fee` | `string, nullable` | Yes | Stake fee (RAO, u64 as decimal string). | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].hotkey_name` | `string, nullable` | Yes | The hotkey's current identity name; `null` when its owner has none. | | `data[].id` | `string` | Yes | Event ID (`{block}-{event_index}` style, unique). | | `data[].is_transfer` | `boolean` | Yes | `true` when this event is a leg of a stake transfer, `false` otherwise. Never null (#1381), unlike the OLD API, which serves null for a non-transfer. | | `data[].netuid` | `integer (int32), nullable` | Yes | Subnet ID. | | `data[].registration_collateral` | `boolean` | Yes | `true` when this stake event is the **miner registration collateral** leg of a burned registration (spec 435+), not a discretionary delegation. The alpha is real staked balance, so the row is kept and flagged rather than dropped; filter on this to exclude it from delegation views. Always `false` before spec 435. | | `data[].slippage` | `string, nullable` | Yes | Signed slippage percentage (decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].transfer_address` | `string, nullable` | Yes | Transfer counterparty address (SS58). | | `data[].usd` | `string, nullable` | Yes | USD value of the TAO leg at the price in force at the event, to two decimal places. `null` when no price covers the row. | | `data[].validator_swap` | `boolean` | Yes | `true` when this row is one leg of a **within-subnet move between validators** (`move_stake`/`swap_stake` with the same origin and destination subnet). From block 6,067,944 the chain settles such a move without touching the pool, so the leg is neither a buy nor a sell; below it the move went through the pool and the leg is a real trade (#911). To list trades, use `trades_only=true` rather than this flag alone. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Swaps DTAO cross-subnet alpha swap events. _Source: https://taostats.io/docs/new/subnets/get-subnets-swaps_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/swaps ``` Requires an API key in the `Authorization` header. DTAO cross-subnet alpha swap events. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/swaps" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/swaps', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/swaps", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `extrinsic_id` | query | `string` | | Filter by extrinsic ID. | | `from_name` | query | `string` | | Filter by source token name (`TAO` or `SN{netuid}`). | | `to_name` | query | `string` | | Filter by destination token name (`TAO` or `SN{netuid}`). | | `tao_value_min` | query | `integer (int64)` | | Minimum TAO value (RAO, inclusive). | | `tao_value_max` | query | `integer (int64)` | | Maximum TAO value (RAO, inclusive). | | `usd_value_min` | query | `string` | | Minimum USD value (inclusive). Compared numerically; a swap with no USD value does not satisfy it. | | `usd_value_max` | query | `string` | | Maximum USD value (inclusive). Compared numerically; a swap with no USD value does not satisfy it. | | `block_number` | query | `integer (int32)` | | Filter by a specific block. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of cross-subnet alpha swaps | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | Coldkey (SS58). | | `data[].extrinsic_id` | `string` | Yes | Extrinsic ID. | | `data[].from_amount` | `string` | Yes | Source amount (RAO, u64 as decimal string). | | `data[].from_name` | `string` | Yes | Source token name (`TAO` or `SN{netuid}`). | | `data[].tao_value` | `string` | Yes | TAO routed through the swap (RAO, u64 as decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].to_amount` | `string` | Yes | Destination amount (RAO, u64 as decimal string). | | `data[].to_name` | `string` | Yes | Destination token name (`TAO` or `SN{netuid}`). | | `data[].usd_value` | `string, nullable` | Yes | USD value of the swap at the price in force when it happened, to two decimal places. `null` when no price covers the row. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Subnets Trades Every dTAO alpha trade: buys, sells and moves. _Source: https://taostats.io/docs/new/subnets/get-subnets-trades_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/subnets/trades ``` Requires an API key in the `Authorization` header. Every dTAO alpha trade: buys, sells and moves. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/subnets/trades" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/subnets/trades', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/subnets/trades", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `coldkey` | query | `string` | | Filter by coldkey (SS58 or 0x-hex). | | `extrinsic_id` | query | `string` | | Filter by extrinsic ID. | | `from_name` | query | `string` | | Filter by source token name (`TAO` or `SN{netuid}`). `from_name=TAO` is the set of buys. | | `to_name` | query | `string` | | Filter by destination token name (`TAO` or `SN{netuid}`). `to_name=TAO` is the set of sells. | | `tao_value_min` | query | `integer (int64)` | | Minimum TAO value, **in RAO** (inclusive). 1 TAO is `1000000000`. | | `tao_value_max` | query | `integer (int64)` | | Maximum TAO value, **in RAO** (inclusive). | | `usd_value_min` | query | `string` | | Minimum USD value (inclusive). Compared numerically; a trade with no USD value does not satisfy it. | | `usd_value_max` | query | `string` | | Maximum USD value (inclusive). Compared numerically; a trade with no USD value does not satisfy it. | | `block_number` | query | `integer (int32)` | | Filter by a specific block. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `timestamp`. One of `timestamp`, `from_amount`, `to_amount`, `tao_value`, `usd_value`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of alpha trades | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].coldkey` | `string` | Yes | The trading account, SS58. | | `data[].extrinsic_id` | `string` | Yes | Extrinsic ID. Not unique: one extrinsic can hold several trades. | | `data[].from_amount` | `string` | Yes | Source amount (RAO, u64 as string). | | `data[].from_name` | `string` | Yes | Source token name: `TAO` or `SN{netuid}`. | | `data[].id` | `string` | Yes | Stable unique row id. The OLD route has none, which is why its consumers key rows on `extrinsic_id` plus `timestamp` — a pair that collides as soon as one extrinsic holds two trades. | | `data[].tao_value` | `string` | Yes | TAO value (RAO, u64 as string): the TAO the chain actually moved. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].to_amount` | `string` | Yes | Destination amount (RAO, u64 as string). | | `data[].to_name` | `string` | Yes | Destination token name: `TAO` or `SN{netuid}`. | | `data[].usd_value` | `string, nullable` | | USD value at the TAO/USD price in force when the trade happened, to two decimal places. `null` when no price covers the row. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Tax Every Taostats API endpoint in the Tax group, with its method and path. _Source: https://taostats.io/docs/new/tax_ _Last reviewed: 2026-10-07_ 2 Tax endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Tax Active Tokens](https://taostats.io/docs/new/tax/get-tax-active-tokens) | `GET` | `/v1/tax/active-tokens` | | [Get Tax Report](https://taostats.io/docs/new/tax/get-tax-report) | `GET` | `/v1/tax/report` | --- # Get Tax Active Tokens The tokens a coldkey has tax rows for in a date range. _Source: https://taostats.io/docs/new/tax/get-tax-active-tokens_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tax/active-tokens ``` Requires an API key in the `Authorization` header. The tokens a coldkey has tax rows for in a date range. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tax/active-tokens" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tax/active-tokens', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tax/active-tokens", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `date_start` | query | `string` | Yes | Start of the range, `YYYY-MM-DD`, inclusive. | | `date_end` | query | `string` | Yes | End of the range, `YYYY-MM-DD`, inclusive. At most 12 calendar months after `date_start`. | | `coldkey` | query | `string` | Yes | Coldkey, SS58 or 0x-prefixed hex. | ## Responses ### `200` — Tokens with rows in the range; empty when there are none | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | `TAO` first if the coldkey has TAO rows in the range, then the subnet tokens by netuid. | | `data[]` | `string` | Yes | | | `pagination` | `object` | Yes | Always one page holding every token. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Tax Report Tax report for one coldkey and one token over a date range. _Source: https://taostats.io/docs/new/tax/get-tax-report_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tax/report ``` Requires an API key in the `Authorization` header. Tax report for one coldkey and one token over a date range. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tax/report" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tax/report', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tax/report", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `token` | query | `string` | Yes | `TAO` (any case) or a subnet token, `SN1`, `SN2`, ... (exact case). | | `date_start` | query | `string` | Yes | Start of the range, `YYYY-MM-DD`, inclusive. | | `date_end` | query | `string` | Yes | End of the range, `YYYY-MM-DD`, inclusive. At most 12 calendar months after `date_start`. | | `coldkey` | query | `string` | Yes | Coldkey, SS58 or 0x-prefixed hex. | | `format` | query | `string` | | `json` (default) or `csv`. CSV is served as a file download. One of `json`, `csv`. | ## Responses ### `200` — Every row of the range; with `format=csv`, the same rows as a CSV download | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].additional_data` | `string, nullable` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].coldkey` | `string` | Yes | Coldkey, SS58. | | `data[].credit_amount` | `string, nullable` | Yes | TAO or alpha, decimal string. | | `data[].daily_income` | `string, nullable` | Yes | TAO or alpha, decimal string. Set on income rows. | | `data[].daily_income_usd` | `string, nullable` | Yes | USD, two decimal places. | | `data[].date` | `string, nullable` | Yes | `YYYY-MM-DD`. Set on income rows and on an invented opening row. | | `data[].debit_amount` | `string, nullable` | Yes | TAO or alpha, decimal string. | | `data[].extrinsic_id` | `string, nullable` | Yes | | | `data[].free_balance` | `string, nullable` | Yes | | | `data[].locked_balance` | `string, nullable` | Yes | | | `data[].network` | `string` | Yes | `finney`, or `nakamoto` / `kusanagi` for the networks before it. | | `data[].reserved_balance` | `string, nullable` | Yes | | | `data[].staked_balance` | `string, nullable` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601, milliseconds, UTC. | | `data[].token` | `string` | Yes | `TAO` or `SN{netuid}`. | | `data[].token_price_in_tao` | `string, nullable` | Yes | Decimal string; `1` for TAO. | | `data[].token_price_in_usd` | `string, nullable` | Yes | Decimal string. | | `data[].total_balance` | `string, nullable` | Yes | TAO or alpha, decimal string. Set on balance rows. | | `data[].transaction_type` | `string, nullable` | Yes | `transfer_in`, `fee`, `token_swap`, ...; `null` on a balance row or an income row. | | `pagination` | `object` | Yes | Always one page holding every row. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `404` — The coldkey has no rows for this token in the range | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Tokenomics Every Taostats API endpoint in the Tokenomics group, with its method and path. _Source: https://taostats.io/docs/new/tokenomics_ _Last reviewed: 2026-10-07_ One Tokenomics endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Get Tokenomics Emission](https://taostats.io/docs/new/tokenomics/get-tokenomics-emission) | `GET` | `/v1/tokenomics/emission` | --- # Get Tokenomics Emission Paginated per-block emission series. _Source: https://taostats.io/docs/new/tokenomics/get-tokenomics-emission_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tokenomics/emission ``` Requires an API key in the `Authorization` header. Paginated per-block emission series. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tokenomics/emission" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tokenomics/emission', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tokenomics/emission", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated per-block emission and total-issuance series | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].emission` | `string` | Yes | Per-block emission at this block, in RAO (u64 as decimal string). | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `data[].total_issuance` | `string` | Yes | Total TAO issued at this block, in RAO (u64 as decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # TradingView Every Taostats API endpoint in the TradingView group, with its method and path. _Source: https://taostats.io/docs/new/tradingview_ _Last reviewed: 2026-10-07_ 3 TradingView endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get TradingView Udf History](https://taostats.io/docs/new/tradingview/get-tradingview-udf-history) | `GET` | `/v1/tradingview/udf/history` | | [Get TradingView Udf Config](https://taostats.io/docs/new/tradingview/get-tradingview-udf-config) | `GET` | `/v1/tradingview/udf/config` | | [Get TradingView Udf Symbol Info](https://taostats.io/docs/new/tradingview/get-tradingview-udf-symbol-info) | `GET` | `/v1/tradingview/udf/symbol_info` | --- # Get TradingView Udf History OHLC candles for charting. _Source: https://taostats.io/docs/new/tradingview/get-tradingview-udf-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tradingview/udf/history ``` Requires an API key in the `Authorization` header. OHLC candles for charting. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tradingview/udf/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tradingview/udf/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tradingview/udf/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `symbol` | query | `string` | Yes | Symbol identifier: `SUB-`, or `SUB--1` for the total series. Required: a missing value is rejected with 400. | | `resolution` | query | `string` | Yes | Resolution token: `1`, `5`, `15`, `60`, `240`, `1D`, `7D`, `30D`. Required: a missing value is rejected with 400. | | `from` | query | `integer (int64)` | | Range start, Unix seconds (inclusive). | | `to` | query | `integer (int64)` | Yes | Range end, Unix seconds (inclusive). Required. | | `countback` | query | `integer (int32)` | | Number of bars to return (most recent within the range). | ## Responses ### `200` — TradingView UDF OHLC history | Field | Type | Required | Description | | --- | --- | --- | --- | | `c` | `array, nullable` | | Close prices. | | `c[]` | `number (double)` | | | | `h` | `array, nullable` | | High prices. | | `h[]` | `number (double)` | | | | `l` | `array, nullable` | | Low prices. | | `l[]` | `number (double)` | | | | `nextTime` | `integer (int64), nullable` | | Next available data timestamp (Unix seconds) when `s = "no_data"`. | | `o` | `array, nullable` | | Open prices. | | `o[]` | `number (double)` | | | | `s` | `string` | Yes | `"ok"` or `"no_data"`. | | `t` | `array, nullable` | | Candle open timestamps (Unix seconds). | | `t[]` | `integer (int64)` | | | | `v` | `array, nullable` | | Per-bar alpha volume traded, parallel-indexed with `t`. Sourced from `dtao_subnet_volume_v1`; `0` for a bar with no trades. | | `v[]` | `number (double)` | | | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get TradingView Udf Config Static charting configuration. _Source: https://taostats.io/docs/new/tradingview/get-tradingview-udf-config_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tradingview/udf/config ``` Requires an API key in the `Authorization` header. Static charting configuration. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tradingview/udf/config" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tradingview/udf/config', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tradingview/udf/config", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters _No parameters._ ## Responses ### `200` — TradingView UDF datafeed configuration | Field | Type | Required | Description | | --- | --- | --- | --- | | `currency_codes` | `array` | Yes | Available quote currencies. | | `currency_codes[].code` | `string` | Yes | Currency code shown in the UI. | | `currency_codes[].description` | `string` | Yes | Human-readable description. | | `currency_codes[].id` | `string` | Yes | Currency identifier. | | `exchanges` | `array` | Yes | Exchanges the datafeed serves. | | `exchanges[]` | `string` | Yes | | | `supported_resolutions` | `array` | Yes | Resolutions the datafeed can serve. | | `supported_resolutions[]` | `string` | Yes | | | `supports_group_request` | `boolean` | Yes | Whether grouped symbol requests are supported. Always `true`. | | `supports_marks` | `boolean` | Yes | Whether the datafeed supplies chart marks. Always `false`. | | `supports_search` | `boolean` | Yes | Whether symbol search is supported. Always `false`. | | `supports_timescale_marks` | `boolean` | Yes | Whether timescale marks are supported. Always `false`. | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get TradingView Udf Symbol Info Symbol metadata for charting. _Source: https://taostats.io/docs/new/tradingview/get-tradingview-udf-symbol-info_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/tradingview/udf/symbol_info ``` Requires an API key in the `Authorization` header. Symbol metadata for charting. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/tradingview/udf/symbol_info" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/tradingview/udf/symbol_info', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/tradingview/udf/symbol_info", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter to a specific subnet netuid. `-1` selects the total series. Omitted: every subnet plus the total series. | ## Responses ### `200` — TradingView UDF symbol metadata | Field | Type | Required | Description | | --- | --- | --- | --- | | `description` | `array` | Yes | Subnet token symbol names, parallel to `symbol`. | | `description[]` | `string` | Yes | | | `exchange-listed` | `string` | Yes | Always `"Bittensor"`. Serialised as `exchange-listed`. | | `exchange-traded` | `string` | Yes | Always `"Bittensor"`. Serialised as `exchange-traded`. | | `has-dwm` | `boolean` | Yes | Whether daily/weekly/monthly resolutions are available. Always `true`. Serialised as `has-dwm` to match the OLD API byte for byte. **Nothing in the charting library reads it** under either spelling — it reads `has-daily` (defaulting to `true`) and `has-weekly-and-monthly` instead — so this field is served for wire compatibility, not for effect. | | `has-intraday` | `boolean` | Yes | Whether intraday resolutions are available. Always `true`. Serialised as `has-intraday`. **This is the field that matters**: the library defaults it to `false` when absent, which disabled the 1, 5, 15, 60 and 240 minute resolutions. | | `minmovement` | `integer (int64)` | Yes | Minimum price movement. Always `1`. | | `pricescale` | `integer (int64)` | Yes | Price scale. Always `1000000`. | | `session` | `string` | Yes | Always `"24x7"`. | | `session-regular` | `string` | Yes | Always `"24x7"`. Serialised as `session-regular`, which is the field the library reads as the symbol's `session` — it does not read `session`. | | `symbol` | `array` | Yes | Symbol identifiers (`SUB-1`, `SUB-2`, ..., `SUB--1`). | | `symbol[]` | `string` | Yes | | | `timezone` | `string` | Yes | Always `"Etc/UTC"`. | | `type` | `array` | Yes | `"crypto"` for each symbol, parallel to `symbol`. | | `type[]` | `string` | Yes | | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Transfers Every Taostats API endpoint in the Transfers group, with its method and path. _Source: https://taostats.io/docs/new/transfers_ _Last reviewed: 2026-10-07_ One Transfers endpoint. | Endpoint | Method | Path | | --- | --- | --- | | [Get Transfers](https://taostats.io/docs/new/transfers/get-transfers) | `GET` | `/v1/transfers` | --- # Get Transfers Paginated list of indexed Balances transfers. _Source: https://taostats.io/docs/new/transfers/get-transfers_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/transfers ``` Requires an API key in the `Authorization` header. Paginated list of indexed Balances transfers. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/transfers" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/transfers', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/transfers", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | `string` | | Network. One of `finney`, `kusanagi`, `nakamoto`. Default: `finney`. One of `finney`, `kusanagi`, `nakamoto`. | | `block_number` | query | `integer (int32)` | | Specific block number (exact match). | | `block_start` | query | `integer (int32)` | | Block range start (inclusive lower bound). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive upper bound). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, Unix seconds (inclusive lower bound). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, Unix seconds (inclusive upper bound). | | `address` | query | `string` | | Match either `from` or `to` (SS58 or 0x-hex; normalized to SS58 before the query). Expands to `(from = ? OR to = ?)`. | | `from` | query | `string` | | Sender address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `to` | query | `string` | | Recipient address (SS58 or 0x-hex; normalized to SS58 before the query). Exact match. | | `amount_min` | query | `string` | | Minimum amount as a decimal string. Inclusive lower bound on `amount` (u64). Invalid decimals are rejected with 400. | | `amount_max` | query | `string` | | Maximum amount as a decimal string. Inclusive upper bound on `amount` (u64). Invalid decimals are rejected with 400. | | `transaction_hash` | query | `string` | | Exact transaction hash (0x-prefixed hex). | | `extrinsic_id` | query | `string` | | Exact extrinsic ID (e.g. `5000000-0002`). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. One of `timestamp`, `amount`. Default: `timestamp`. One of `timestamp`, `amount`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated list of transfers matching the filter set | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].amount` | `string` | Yes | Transfer amount as a decimal string (u64). | | `data[].block_number` | `integer (int32)` | Yes | Block height. | | `data[].extrinsic_id` | `string` | Yes | Parent extrinsic ID. | | `data[].fee` | `string` | Yes | Fee of the **parent extrinsic** as a decimal string (u64) — not of this transfer. An extrinsic pays one fee, so an extrinsic that produces several transfers (a batch) repeats the same value on every one of its rows, and summing `fee` across rows over-counts: deduplicate by `extrinsic_id` first. The OLD API does the same, so this is parity, not a defect (#577). | | `data[].from` | `string` | Yes | Sender SS58 address. | | `data[].id` | `string` | Yes | Transfer ID. | | `data[].network` | `string` | Yes | Network name. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision, e.g. `"2024-05-15T10:30:00.000Z"`. | | `data[].to` | `string` | Yes | Recipient SS58 address. | | `data[].transaction_hash` | `string` | Yes | Hash of the parent extrinsic (0x-prefixed hex). Per-extrinsic in the same way `fee` is, so a batch repeats it across its rows. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Validators Every Taostats API endpoint in the Validators group, with its method and path. _Source: https://taostats.io/docs/new/validators_ _Last reviewed: 2026-10-07_ 14 Validators endpoints. | Endpoint | Method | Path | | --- | --- | --- | | [Get Validators](https://taostats.io/docs/new/validators/get-validators) | `GET` | `/v1/validators` | | [Get Validators Active](https://taostats.io/docs/new/validators/get-validators-active) | `GET` | `/v1/validators/active` | | [Get Validators Baskets History](https://taostats.io/docs/new/validators/get-validators-baskets-history) | `GET` | `/v1/validators/baskets/history` | | [Get Validators Baskets](https://taostats.io/docs/new/validators/get-validators-baskets) | `GET` | `/v1/validators/baskets` | | [Get Validators Hotkey History](https://taostats.io/docs/new/validators/get-validators-hotkey-history) | `GET` | `/v1/validators/{hotkey}/history` | | [Get Validators Hotkey Family History](https://taostats.io/docs/new/validators/get-validators-hotkey-family-history) | `GET` | `/v1/validators/hotkey-family/history` | | [Get Validators Hotkey Family](https://taostats.io/docs/new/validators/get-validators-hotkey-family) | `GET` | `/v1/validators/hotkey-family` | | [Get Validators Hotkey History Pre dTAO](https://taostats.io/docs/new/validators/get-validators-hotkey-history-pre-dtao) | `GET` | `/v1/validators/{hotkey}/history/pre-dtao` | | [Get Validators Performance Hotkey History](https://taostats.io/docs/new/validators/get-validators-performance-hotkey-history) | `GET` | `/v1/validators/performance/{hotkey}/history` | | [Get Validators Performance Hotkey](https://taostats.io/docs/new/validators/get-validators-performance-hotkey) | `GET` | `/v1/validators/performance/{hotkey}` | | [Get Validators Weights History](https://taostats.io/docs/new/validators/get-validators-weights-history) | `GET` | `/v1/validators/weights/history` | | [Get Validators Weights](https://taostats.io/docs/new/validators/get-validators-weights) | `GET` | `/v1/validators/weights` | | [Get Validators Yield](https://taostats.io/docs/new/validators/get-validators-yield) | `GET` | `/v1/validators/yield` | | [Post Validators Yield](https://taostats.io/docs/new/validators/post-validators-yield) | `POST` | `/v1/validators/yield` | --- # Get Validators Current validator leaderboard. _Source: https://taostats.io/docs/new/validators/get-validators_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators ``` Requires an API key in the `Authorization` header. Current validator leaderboard. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter to a single hotkey (SS58 or 0x-hex). Normalized to SS58. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `rank`. One of `rank`, `root_rank`, `alpha_rank`, `active_subnets`, `global_nominators`, `take`, `global_weighted_stake`, `global_alpha_stake_as_tao`, `weighted_root_stake`, `root_stake`, `dominance`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated validator leaderboard | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].active_subnets` | `integer (int32)` | Yes | | | `data[].alpha_rank` | `integer (int32)` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Snapshot block height. | | `data[].coldkey` | `string` | Yes | Validator coldkey (SS58). | | `data[].created_on_date` | `string, nullable` | Yes | Date (`YYYY-MM-DD`, UTC) the hotkey first registered as a neuron, or Finney's first day for a neuron the chain started with. `null` when the hotkey had never registered by this snapshot — or, on the latest snapshot only, for a registration so recent the registration table had not reached it yet; the next tick fills it. | | `data[].dominance` | `string` | Yes | Dominance percentage (0–100), decimal string. | | `data[].dominance_24_hr_change` | `string, nullable` | Yes | The same 24-hour comparison for `dominance`, percentage points, signed decimal string. | | `data[].global_alpha_stake_as_tao` | `string` | Yes | rao, decimal string. | | `data[].global_nominators` | `integer (int32)` | Yes | | | `data[].global_nominators_24_hr_change` | `integer (int32), nullable` | Yes | Change in `global_nominators` over the 24 hours before this snapshot. `null` when there is nothing 24 hours back to compare against, which recall's own field is too. A history row measures against the previous daily snapshot, exactly as recall does; the latest row measures against the figures the newest tick at or before 7,200 blocks back stored, and where no tick in the day below that exists — the first day after this shipped, or a longer `validators-latest` outage — against the newest daily snapshot instead (#900). | | `data[].global_weighted_stake` | `string` | Yes | rao, decimal string. | | `data[].global_weighted_stake_24_hr_change` | `string, nullable` | Yes | rao, decimal string, signed. The same 24-hour comparison for `global_weighted_stake`. | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`, joined by coldkey). `null` when the coldkey has no identity set. | | `data[].nominator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].rank` | `integer (int32)` | Yes | | | `data[].root_rank` | `integer (int32)` | Yes | | | `data[].root_stake` | `string` | Yes | rao, decimal string. | | `data[].take` | `string` | Yes | Validator take as a decimal-string fraction of 1. | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `data[].validator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].weighted_root_stake` | `string` | Yes | rao, decimal string. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Active Active validators per subnet. _Source: https://taostats.io/docs/new/validators/get-validators-active_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/active ``` Requires an API key in the `Authorization` header. Active validators per subnet. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/active" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/active', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/active", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | ## Responses ### `200` — Active validators per subnet | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].address` | `string` | Yes | Validator address (SS58). | | `data[].hotkey_alpha` | `string` | Yes | Hotkey alpha — `TotalHotkeyAlpha` on the subnet — as a rao decimal string. | | `data[].name` | `string, nullable` | Yes | Validator identity name, from the on-chain `IdentitiesV2` account identity — the same source and the same join as the four sibling validator routes (#837). `null` when the hotkey's coldkey has no identity set. | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID (root rows carry `0`). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Baskets History Daily beta-basket history. _Source: https://taostats.io/docs/new/validators/get-validators-baskets-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/baskets/history ``` Requires an API key in the `Authorization` header. Daily beta-basket history. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/baskets/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/baskets/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/baskets/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter to one validator hotkey (SS58 or `0x`-hex). | | `timestamp_start` | query | `integer (int64)` | | Range start, unix seconds (inclusive). Resolved to the **UTC day** the instant falls in, since rows are one-per-day. | | `timestamp_end` | query | `integer (int64)` | | Range end, unix seconds (inclusive). Resolved to the **UTC day** the instant falls in, so the whole of that day is included. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `day`. One of `day`, `nav_tao`, `spot_nav_tao`, `shares`, `nav_per_share`, `performance`, `return_7d`, `return_30d`, `staker_return_7d`, `staker_return_30d`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Daily beta-basket history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block the snapshot was read at. | | `data[].day` | `string` | Yes | UTC date of `timestamp` (`YYYY-MM-DD`). On the history endpoint it is the row's identity — the day whose closing block this snapshot is. | | `data[].deposited_tao` | `string` | Yes | Lifetime realizable TAO deposited into the fund (RAO, u64 as decimal string). | | `data[].holdings` | `array` | Yes | Per-subnet holdings, each valued at spot and realizable. | | `data[].holdings[].alpha` | `string` | Yes | Alpha held (RAO, u64 as decimal string). | | `data[].holdings[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].holdings[].realizable_tao` | `string` | Yes | Slippage-aware redemption quote net of fees (RAO, u64 as decimal string). This, not `spot_tao`, is what `nav_tao` is the sum of. | | `data[].holdings[].spot_tao` | `string` | Yes | Spot mark, `price × alpha` (RAO, u64 as decimal string). **Display only** — the chain itself says spot "is exposed for display/analytics only and is never used for share pricing or redemption sizing". | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].nav_per_share` | `number (double), nullable` | Yes | `nav_tao / shares`. **`null` when the fund has no shares outstanding** — no supply means no share price, and `0.0` would read as "worthless". | | `data[].nav_tao` | `string` | Yes | **Realizable** (slippage-aware) NAV — the fund's redemption value, and the basis of every derived figure here (RAO, u64 as decimal string). | | `data[].performance` | `number (double), nullable` | Yes | `(nav_tao + redeemed_tao) / deposited_tao` — the lifetime multiple, 1.0 is break-even. `null` when nothing was ever deposited. | | `data[].rate` | `number (double)` | Yes | `SubtensorModule::BasketRate` — cumulative fund shares accrued per unit of root stake. **Never null**: the chain declares the map `ValueQuery` with a zero default, so an absent key is a real `0`. | | `data[].redeemed_tao` | `string` | Yes | Lifetime TAO redeemed out of the fund (RAO, u64 as decimal string). | | `data[].return_30d` | `number (double), nullable` | Yes | As `return_7d`, over 30 days. | | `data[].return_7d` | `number (double), nullable` | Yes | Capital return on `nav_per_share` over 7 days — **not annualised**, and legitimately negative. `null` until the window has an anchor. | | `data[].shares` | `string` | Yes | Outstanding fund shares (u64 as decimal string). | | `data[].spot_nav_tao` | `string` | Yes | Spot-marked NAV, `Σ price × alpha` (RAO, u64 as decimal string) — the headline mark only. | | `data[].staker_return_30d` | `number (double), nullable` | Yes | As `staker_return_7d`, over 30 days. | | `data[].staker_return_7d` | `number (double), nullable` | Yes | Staker total return over 7 days, `twr / twr_then − 1` — income and mark-to-market together, **not annualised**, defined only within one fund life. `null` when either sample is missing or the two are from different lives. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].twr` | `number (double), nullable` | Yes | `SubtensorModule::BasketTwr` — the fund's staker total-return accumulator, 1.0 at each fund life's baseline. `null` on a runtime before release 450, which introduced it. | | `data[].twr_first_block` | `string, nullable` | Yes | `BetaBaseline.first_block` — the block this fund's **current life** was stamped at (u64 as decimal string). Two `twr` samples are comparable only within one life. `null` pre-450 and for a fund the chain calls *provisional* (shares outstanding, no baseline stamped) — never `0`, which is a block number. | | `data[].weights` | `array` | Yes | The validator's root weight vector — its curation strategy, as stored. Empty for every validator while `RootWeightSettingEnabled` is off. | | `data[].weights[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].weights[].weight` | `integer (int32)` | Yes | Raw chain weight. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Baskets Current beta-basket snapshot per validator. _Source: https://taostats.io/docs/new/validators/get-validators-baskets_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/baskets ``` Requires an API key in the `Authorization` header. Current beta-basket snapshot per validator. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/baskets" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/baskets', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/baskets", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter to one validator hotkey (SS58 or `0x`-hex). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `nav_tao`. One of `nav_tao`, `spot_nav_tao`, `shares`, `nav_per_share`, `performance`, `return_7d`, `return_30d`, `staker_return_7d`, `staker_return_30d`, `hotkey`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Current beta-basket snapshot per validator | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block the snapshot was read at. | | `data[].day` | `string` | Yes | UTC date of `timestamp` (`YYYY-MM-DD`). On the history endpoint it is the row's identity — the day whose closing block this snapshot is. | | `data[].deposited_tao` | `string` | Yes | Lifetime realizable TAO deposited into the fund (RAO, u64 as decimal string). | | `data[].holdings` | `array` | Yes | Per-subnet holdings, each valued at spot and realizable. | | `data[].holdings[].alpha` | `string` | Yes | Alpha held (RAO, u64 as decimal string). | | `data[].holdings[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].holdings[].realizable_tao` | `string` | Yes | Slippage-aware redemption quote net of fees (RAO, u64 as decimal string). This, not `spot_tao`, is what `nav_tao` is the sum of. | | `data[].holdings[].spot_tao` | `string` | Yes | Spot mark, `price × alpha` (RAO, u64 as decimal string). **Display only** — the chain itself says spot "is exposed for display/analytics only and is never used for share pricing or redemption sizing". | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].nav_per_share` | `number (double), nullable` | Yes | `nav_tao / shares`. **`null` when the fund has no shares outstanding** — no supply means no share price, and `0.0` would read as "worthless". | | `data[].nav_tao` | `string` | Yes | **Realizable** (slippage-aware) NAV — the fund's redemption value, and the basis of every derived figure here (RAO, u64 as decimal string). | | `data[].performance` | `number (double), nullable` | Yes | `(nav_tao + redeemed_tao) / deposited_tao` — the lifetime multiple, 1.0 is break-even. `null` when nothing was ever deposited. | | `data[].rate` | `number (double)` | Yes | `SubtensorModule::BasketRate` — cumulative fund shares accrued per unit of root stake. **Never null**: the chain declares the map `ValueQuery` with a zero default, so an absent key is a real `0`. | | `data[].redeemed_tao` | `string` | Yes | Lifetime TAO redeemed out of the fund (RAO, u64 as decimal string). | | `data[].return_30d` | `number (double), nullable` | Yes | As `return_7d`, over 30 days. | | `data[].return_7d` | `number (double), nullable` | Yes | Capital return on `nav_per_share` over 7 days — **not annualised**, and legitimately negative. `null` until the window has an anchor. | | `data[].shares` | `string` | Yes | Outstanding fund shares (u64 as decimal string). | | `data[].spot_nav_tao` | `string` | Yes | Spot-marked NAV, `Σ price × alpha` (RAO, u64 as decimal string) — the headline mark only. | | `data[].staker_return_30d` | `number (double), nullable` | Yes | As `staker_return_7d`, over 30 days. | | `data[].staker_return_7d` | `number (double), nullable` | Yes | Staker total return over 7 days, `twr / twr_then − 1` — income and mark-to-market together, **not annualised**, defined only within one fund life. `null` when either sample is missing or the two are from different lives. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].twr` | `number (double), nullable` | Yes | `SubtensorModule::BasketTwr` — the fund's staker total-return accumulator, 1.0 at each fund life's baseline. `null` on a runtime before release 450, which introduced it. | | `data[].twr_first_block` | `string, nullable` | Yes | `BetaBaseline.first_block` — the block this fund's **current life** was stamped at (u64 as decimal string). Two `twr` samples are comparable only within one life. `null` pre-450 and for a fund the chain calls *provisional* (shares outstanding, no baseline stamped) — never `0`, which is a block number. | | `data[].weights` | `array` | Yes | The validator's root weight vector — its curation strategy, as stored. Empty for every validator while `RootWeightSettingEnabled` is off. | | `data[].weights[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].weights[].weight` | `integer (int32)` | Yes | Raw chain weight. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Hotkey History Validator snapshots over time. _Source: https://taostats.io/docs/new/validators/get-validators-hotkey-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/{hotkey}/history ``` Requires an API key in the `Authorization` header. Validator snapshots over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/{hotkey}/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/{hotkey}/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/{hotkey}/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | path | `string` | Yes | Validator hotkey (SS58 or 0x-hex) | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated validator history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].active_subnets` | `integer (int32)` | Yes | | | `data[].alpha_rank` | `integer (int32)` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Snapshot block height. | | `data[].coldkey` | `string` | Yes | Validator coldkey (SS58). | | `data[].created_on_date` | `string, nullable` | Yes | Date (`YYYY-MM-DD`, UTC) the hotkey first registered as a neuron, or Finney's first day for a neuron the chain started with. `null` when the hotkey had never registered by this snapshot — or, on the latest snapshot only, for a registration so recent the registration table had not reached it yet; the next tick fills it. | | `data[].dominance` | `string` | Yes | Dominance percentage (0–100), decimal string. | | `data[].dominance_24_hr_change` | `string, nullable` | Yes | The same 24-hour comparison for `dominance`, percentage points, signed decimal string. | | `data[].global_alpha_stake_as_tao` | `string` | Yes | rao, decimal string. | | `data[].global_nominators` | `integer (int32)` | Yes | | | `data[].global_nominators_24_hr_change` | `integer (int32), nullable` | Yes | Change in `global_nominators` over the 24 hours before this snapshot. `null` when there is nothing 24 hours back to compare against, which recall's own field is too. A history row measures against the previous daily snapshot, exactly as recall does; the latest row measures against the figures the newest tick at or before 7,200 blocks back stored, and where no tick in the day below that exists — the first day after this shipped, or a longer `validators-latest` outage — against the newest daily snapshot instead (#900). | | `data[].global_weighted_stake` | `string` | Yes | rao, decimal string. | | `data[].global_weighted_stake_24_hr_change` | `string, nullable` | Yes | rao, decimal string, signed. The same 24-hour comparison for `global_weighted_stake`. | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`, joined by coldkey). `null` when the coldkey has no identity set. | | `data[].nominator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].rank` | `integer (int32)` | Yes | | | `data[].root_rank` | `integer (int32)` | Yes | | | `data[].root_stake` | `string` | Yes | rao, decimal string. | | `data[].take` | `string` | Yes | Validator take as a decimal-string fraction of 1. | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `data[].validator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].weighted_root_stake` | `string` | Yes | rao, decimal string. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Hotkey Family History Family trees over time. _Source: https://taostats.io/docs/new/validators/get-validators-hotkey-family-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/hotkey-family/history ``` Requires an API key in the `Authorization` header. Family trees over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/hotkey-family/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/hotkey-family/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/hotkey-family/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter to a single hotkey (SS58 or 0x-hex). Normalized to SS58. | | `netuid` | query | `integer (int32)` | | Filter to a single subnet. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated hotkey family history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_stake` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].childkey_take` | `string` | Yes | | | `data[].children` | `array` | Yes | | | `data[].children[].alpha_stake` | `string` | Yes | | | `data[].children[].childkey_take` | `string` | Yes | | | `data[].children[].coldkey` | `string` | Yes | | | `data[].children[].family_alpha_stake` | `string` | Yes | | | `data[].children[].family_root_stake` | `string` | Yes | | | `data[].children[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].children[].family_total_alpha_stake` | `string` | Yes | | | `data[].children[].hotkey` | `string` | Yes | | | `data[].children[].proportion` | `string` | Yes | | | `data[].children[].proportion_alpha_stake` | `string` | Yes | | | `data[].children[].proportion_root_stake` | `string` | Yes | | | `data[].children[].proportion_root_stake_as_alpha` | `string` | Yes | | | `data[].children[].proportion_staked` | `string` | Yes | | | `data[].children[].proportion_total_alpha_stake` | `string` | Yes | | | `data[].children[].root_stake` | `string` | Yes | | | `data[].children[].root_stake_as_alpha` | `string` | Yes | | | `data[].children[].root_weight` | `string` | Yes | | | `data[].children[].take` | `string` | Yes | | | `data[].children[].total_alpha_stake` | `string` | Yes | | | `data[].coldkey` | `string` | Yes | | | `data[].family_alpha_stake` | `string` | Yes | | | `data[].family_root_stake` | `string` | Yes | | | `data[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].family_total_alpha_stake` | `string` | Yes | | | `data[].hotkey` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].parents` | `array` | Yes | | | `data[].parents[].alpha_stake` | `string` | Yes | | | `data[].parents[].childkey_take` | `string` | Yes | | | `data[].parents[].coldkey` | `string` | Yes | | | `data[].parents[].family_alpha_stake` | `string` | Yes | | | `data[].parents[].family_root_stake` | `string` | Yes | | | `data[].parents[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].family_total_alpha_stake` | `string` | Yes | | | `data[].parents[].hotkey` | `string` | Yes | | | `data[].parents[].proportion` | `string` | Yes | | | `data[].parents[].proportion_alpha_stake` | `string` | Yes | | | `data[].parents[].proportion_root_stake` | `string` | Yes | | | `data[].parents[].proportion_root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].proportion_staked` | `string` | Yes | | | `data[].parents[].proportion_total_alpha_stake` | `string` | Yes | | | `data[].parents[].root_stake` | `string` | Yes | | | `data[].parents[].root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].root_weight` | `string` | Yes | | | `data[].parents[].take` | `string` | Yes | | | `data[].parents[].total_alpha_stake` | `string` | Yes | | | `data[].root_stake` | `string` | Yes | | | `data[].root_stake_as_alpha` | `string` | Yes | | | `data[].root_weight` | `string` | Yes | | | `data[].take` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | | | `data[].total_alpha_stake` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Hotkey Family Current family trees. _Source: https://taostats.io/docs/new/validators/get-validators-hotkey-family_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/hotkey-family ``` Requires an API key in the `Authorization` header. Current family trees. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/hotkey-family" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/hotkey-family', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/hotkey-family", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter to a single hotkey (SS58 or 0x-hex). Normalized to SS58. | | `netuid` | query | `integer (int32)` | | Filter to a single subnet. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | ## Responses ### `200` — Paginated hotkey family trees | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha_stake` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].childkey_take` | `string` | Yes | | | `data[].children` | `array` | Yes | | | `data[].children[].alpha_stake` | `string` | Yes | | | `data[].children[].childkey_take` | `string` | Yes | | | `data[].children[].coldkey` | `string` | Yes | | | `data[].children[].family_alpha_stake` | `string` | Yes | | | `data[].children[].family_root_stake` | `string` | Yes | | | `data[].children[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].children[].family_total_alpha_stake` | `string` | Yes | | | `data[].children[].hotkey` | `string` | Yes | | | `data[].children[].proportion` | `string` | Yes | | | `data[].children[].proportion_alpha_stake` | `string` | Yes | | | `data[].children[].proportion_root_stake` | `string` | Yes | | | `data[].children[].proportion_root_stake_as_alpha` | `string` | Yes | | | `data[].children[].proportion_staked` | `string` | Yes | | | `data[].children[].proportion_total_alpha_stake` | `string` | Yes | | | `data[].children[].root_stake` | `string` | Yes | | | `data[].children[].root_stake_as_alpha` | `string` | Yes | | | `data[].children[].root_weight` | `string` | Yes | | | `data[].children[].take` | `string` | Yes | | | `data[].children[].total_alpha_stake` | `string` | Yes | | | `data[].coldkey` | `string` | Yes | | | `data[].family_alpha_stake` | `string` | Yes | | | `data[].family_root_stake` | `string` | Yes | | | `data[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].family_total_alpha_stake` | `string` | Yes | | | `data[].hotkey` | `string` | Yes | | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].parents` | `array` | Yes | | | `data[].parents[].alpha_stake` | `string` | Yes | | | `data[].parents[].childkey_take` | `string` | Yes | | | `data[].parents[].coldkey` | `string` | Yes | | | `data[].parents[].family_alpha_stake` | `string` | Yes | | | `data[].parents[].family_root_stake` | `string` | Yes | | | `data[].parents[].family_root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].family_total_alpha_stake` | `string` | Yes | | | `data[].parents[].hotkey` | `string` | Yes | | | `data[].parents[].proportion` | `string` | Yes | | | `data[].parents[].proportion_alpha_stake` | `string` | Yes | | | `data[].parents[].proportion_root_stake` | `string` | Yes | | | `data[].parents[].proportion_root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].proportion_staked` | `string` | Yes | | | `data[].parents[].proportion_total_alpha_stake` | `string` | Yes | | | `data[].parents[].root_stake` | `string` | Yes | | | `data[].parents[].root_stake_as_alpha` | `string` | Yes | | | `data[].parents[].root_weight` | `string` | Yes | | | `data[].parents[].take` | `string` | Yes | | | `data[].parents[].total_alpha_stake` | `string` | Yes | | | `data[].root_stake` | `string` | Yes | | | `data[].root_stake_as_alpha` | `string` | Yes | | | `data[].root_weight` | `string` | Yes | | | `data[].take` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | | | `data[].total_alpha_stake` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Hotkey History Pre dTAO Stake and nominators at every end-of-day snapshot before dTAO. _Source: https://taostats.io/docs/new/validators/get-validators-hotkey-history-pre-dtao_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/{hotkey}/history/pre-dtao ``` Requires an API key in the `Authorization` header. Stake and nominators at every end-of-day snapshot before dTAO. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/{hotkey}/history/pre-dtao" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/{hotkey}/history/pre-dtao', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/{hotkey}/history/pre-dtao", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | path | `string` | Yes | Validator hotkey (SS58 or 0x-hex) | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, unix seconds (inclusive). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated pre-dTAO stake history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | The snapshot block. | | `data[].hotkey` | `string` | Yes | Hotkey (SS58). | | `data[].nominators` | `integer (int32)` | Yes | Coldkeys holding non-zero stake on the hotkey. Zero-value entries the chain kept after a full unstake are not counted. | | `data[].stake` | `string` | Yes | rao, as a decimal string: the sum of the hotkey's `Stake` entries, which equals the chain's `TotalHotkeyStake` at the block. | | `data[].timestamp` | `string` | Yes | The snapshot block's timestamp, ISO 8601 with milliseconds. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Performance Hotkey History Performance over time. _Source: https://taostats.io/docs/new/validators/get-validators-performance-hotkey-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/performance/{hotkey}/history ``` Requires an API key in the `Authorization` header. Performance over time. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/performance/{hotkey}/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/performance/{hotkey}/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/performance/{hotkey}/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | path | `string` | Yes | Validator hotkey (SS58 or 0x-hex) | | `netuid` | query | `integer (int32)` | | Filter to a single subnet. | | `block_start` | query | `integer (int32)` | | Block range start (inclusive). | | `block_end` | query | `integer (int32)` | | Block range end (inclusive). | | `timestamp_start` | query | `integer (int64)` | | Timestamp range start, unix seconds (inclusive). | | `timestamp_end` | query | `integer (int64)` | | Timestamp range end, unix seconds (inclusive). | | `scope` | query | `string` | | Which rows to return. `hotkey` (default): every row whose `hotkey` is the path hotkey, including its rows as somebody else's child. `own_and_children`: the hotkey's own `running_infra` rows plus the `childkey` rows of its children (`parent_hotkey` = the path hotkey). One of `hotkey`, `own_and_children`. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Only `timestamp`. Default: `timestamp`. One of `timestamp`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated performance history | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].childkey_take` | `string` | Yes | | | `data[].coldkey` | `string` | Yes | Validator coldkey (SS58). On a `childkey` row this is the **parent's** coldkey, not the owner of the child `hotkey` (the OLD API does the same). | | `data[].dividends` | `string` | Yes | | | `data[].dominance` | `string` | Yes | | | `data[].family_alpha` | `string` | Yes | | | `data[].family_root_weight` | `string` | Yes | | | `data[].family_subnet_weight` | `string` | Yes | | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). For a `childkey` row this is the child. | | `data[].last_updated` | `integer (int32)` | Yes | Block of the most recent update. | | `data[].mech_last_updated` | `array` | Yes | Per-mechanism last-update blocks. | | `data[].mech_last_updated[]` | `integer (int32)` | Yes | | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`) of the coldkey that owns `hotkey`. On a `running_infra` row that is `coldkey`; on a `childkey` row it is the child's own owner, not `coldkey`. `null` when that owner has no identity set. | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].nominator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].nominators` | `integer (int32)` | Yes | | | `data[].parent_hotkey` | `string, nullable` | Yes | Parent validator hotkey (SS58) for a `childkey` row; `null` for a `running_infra` row (the hotkey is acting on its own, no parent). Distinguishes the per-parent rows a child with multiple parents produces — they share `(hotkey, netuid, uid)` and differ only here. | | `data[].position` | `integer (int32)` | Yes | | | `data[].proportion` | `string` | Yes | | | `data[].ratio` | `string` | Yes | | | `data[].root_weight` | `string` | Yes | | | `data[].subnet_weight` | `string` | Yes | | | `data[].take` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `data[].uid` | `integer (int32)` | Yes | Metagraph UID; `-1` when the hotkey is not in the subnet's metagraph. | | `data[].validator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].validator_type` | `string` | Yes | `running_infra` or `childkey`. | | `data[].vtrust` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Performance Hotkey Current per-subnet performance. _Source: https://taostats.io/docs/new/validators/get-validators-performance-hotkey_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/performance/{hotkey} ``` Requires an API key in the `Authorization` header. Current per-subnet performance. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/performance/{hotkey}" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/performance/{hotkey}', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/performance/{hotkey}", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | path | `string` | Yes | Validator hotkey (SS58 or 0x-hex) | | `netuid` | query | `integer (int32)` | | Filter to a single subnet. | | `validator_type` | query | `string` | | Filter by validator type (`running_infra` or `childkey`). One of `running_infra`, `childkey`. | | `scope` | query | `string` | | Which rows to return. `hotkey` (default): every row whose `hotkey` is the path hotkey, including its rows as somebody else's child. `own_and_children`: the hotkey's own `running_infra` rows plus the `childkey` rows of its children (`parent_hotkey` = the path hotkey). One of `hotkey`, `own_and_children`. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `netuid`. One of `netuid`, `uid`, `position`, `last_updated`, `nominators`, `vtrust`, `validator_type`, `take`, `childkey_take`, `proportion`, `subnet_weight`, `root_weight`, `alpha`, `family_subnet_weight`, `family_root_weight`, `family_alpha`, `dominance`, `dividends`, `ratio`, `nominator_return_per_day`, `validator_return_per_day`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated per-subnet performance | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].alpha` | `string` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].childkey_take` | `string` | Yes | | | `data[].coldkey` | `string` | Yes | Validator coldkey (SS58). On a `childkey` row this is the **parent's** coldkey, not the owner of the child `hotkey` (the OLD API does the same). | | `data[].dividends` | `string` | Yes | | | `data[].dominance` | `string` | Yes | | | `data[].family_alpha` | `string` | Yes | | | `data[].family_root_weight` | `string` | Yes | | | `data[].family_subnet_weight` | `string` | Yes | | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). For a `childkey` row this is the child. | | `data[].last_updated` | `integer (int32)` | Yes | Block of the most recent update. | | `data[].mech_last_updated` | `array` | Yes | Per-mechanism last-update blocks. | | `data[].mech_last_updated[]` | `integer (int32)` | Yes | | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`) of the coldkey that owns `hotkey`. On a `running_infra` row that is `coldkey`; on a `childkey` row it is the child's own owner, not `coldkey`. `null` when that owner has no identity set. | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].nominator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].nominators` | `integer (int32)` | Yes | | | `data[].parent_hotkey` | `string, nullable` | Yes | Parent validator hotkey (SS58) for a `childkey` row; `null` for a `running_infra` row (the hotkey is acting on its own, no parent). Distinguishes the per-parent rows a child with multiple parents produces — they share `(hotkey, netuid, uid)` and differ only here. | | `data[].position` | `integer (int32)` | Yes | | | `data[].proportion` | `string` | Yes | | | `data[].ratio` | `string` | Yes | | | `data[].root_weight` | `string` | Yes | | | `data[].subnet_weight` | `string` | Yes | | | `data[].take` | `string` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `data[].uid` | `integer (int32)` | Yes | Metagraph UID; `-1` when the hotkey is not in the subnet's metagraph. | | `data[].validator_return_per_day` | `string` | Yes | rao/day, decimal string. | | `data[].validator_type` | `string` | Yes | `running_infra` or `childkey`. | | `data[].vtrust` | `string` | Yes | | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Weights History GET /v1/validators/weights/history — Taostats API endpoint in the Validators group. _Source: https://taostats.io/docs/new/validators/get-validators-weights-history_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/weights/history ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/weights/history" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/weights/history', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/weights/history", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | | | `netuid` | query | `integer (int32)` | | | | `mechanism` | query | `integer (int32)` | | Subnet mechanism (0-15). Default: 0, the main mechanism. | | `uid` | query | `integer (int32)` | | | | `block_number` | query | `integer (int32)` | | | | `block_start` | query | `integer (int32)` | | | | `block_end` | query | `integer (int32)` | | | | `timestamp_start` | query | `integer (int64)` | | | | `timestamp_end` | query | `integer (int64)` | | | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | | | `order_by` | query | `string` | | Order column: `timestamp` (only). | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Validator weight assignments over time | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].mechanism` | `integer (int32)` | Yes | Subnet mechanism the weights were set on (0 is the main mechanism). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].uid` | `integer (int32), nullable` | | Validator neuron UID. | | `data[].weights` | `array` | Yes | Weight assignments set by this validator. | | `data[].weights[].hotkey` | `string` | Yes | Target hotkey (SS58), or `"unknown"` if the UID has no live neuron. | | `data[].weights[].uid` | `integer (int32)` | Yes | Target neuron UID. | | `data[].weights[].weight` | `string` | Yes | Weight value (sum-normalised fraction, decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Weights GET /v1/validators/weights — Taostats API endpoint in the Validators group. _Source: https://taostats.io/docs/new/validators/get-validators-weights_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/weights ``` Requires an API key in the `Authorization` header. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/weights" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/weights', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/weights", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter by validator hotkey (SS58 or 0x-hex). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `mechanism` | query | `integer (int32)` | | Subnet mechanism (0-15). Default: 0, the main mechanism. | | `uid` | query | `integer (int32)` | | Filter by validator neuron UID. | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | | | `order_by` | query | `string` | | Order column: `netuid` (default) or `uid`. | | `order_dir` | query | `string` | | Sort direction. Default: `asc`. One of `asc`, `desc`. | ## Responses ### `200` — Current validator weight assignments | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | Block height of the snapshot. | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].mechanism` | `integer (int32)` | Yes | Subnet mechanism the weights were set on (0 is the main mechanism). | | `data[].netuid` | `integer (int32)` | Yes | Subnet ID. | | `data[].timestamp` | `string` | Yes | ISO 8601 timestamp with millisecond precision. | | `data[].uid` | `integer (int32), nullable` | | Validator neuron UID. | | `data[].weights` | `array` | Yes | Weight assignments set by this validator. | | `data[].weights[].hotkey` | `string` | Yes | Target hotkey (SS58), or `"unknown"` if the UID has no live neuron. | | `data[].weights[].uid` | `integer (int32)` | Yes | Target neuron UID. | | `data[].weights[].weight` | `string` | Yes | Weight value (sum-normalised fraction, decimal string). | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid query parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Get Validators Yield Validator yield/APY per subnet, latest snapshot. _Source: https://taostats.io/docs/new/validators/get-validators-yield_ _Last reviewed: 2026-10-07_ ```http GET https://api.taostats.io/v1/validators/yield ``` Requires an API key in the `Authorization` header. Validator yield/APY per subnet, latest snapshot. ## Try it Try this request in the browser on the HTML version of this page, or use the code samples below. ### Code samples **cURL** ```bash curl -H "Authorization: " \ "https://api.taostats.io/v1/validators/yield" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/yield', { headers: { Authorization: '', }, }); const data = await response.json(); ``` **Python** ```python import requests response = requests.get( "https://api.taostats.io/v1/validators/yield", headers={"Authorization": ""}, ) data = response.json() ``` ## Parameters | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `hotkey` | query | `string` | | Filter by hotkey (SS58 or 0x-hex). | | `netuid` | query | `integer (int32)` | | Filter by subnet ID. | | `min_stake` | query | `integer (int64)` | | Minimum stake filter (u64 rao). | | `page` | query | `integer (int32)` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `limit` | query | `integer (int32)` | | Results per page. Default: 50. Max: 200. | | `order_by` | query | `string` | | Column to order by. Default: `stake`. One of `stake`, `netuid`, `name`, `one_hour_apy`, `one_day_apy`, `seven_day_apy`, `thirty_day_apy`. | | `order_dir` | query | `string` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | ## Responses ### `200` — Paginated validator yield | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`, joined by coldkey). `null` when the coldkey has no identity set. | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].one_day_apy` | `number (double)` | Yes | | | `data[].one_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].one_hour_apy` | `number (double)` | Yes | | | `data[].seven_day_apy` | `number (double)` | Yes | | | `data[].seven_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].stake` | `string` | Yes | Stake (`total_hotkey_alpha`), u64 rao as a decimal string. | | `data[].thirty_day_apy` | `number (double)` | Yes | | | `data[].thirty_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api). --- # Post Validators Yield Bulk yield for specific (hotkey, netuid) positions. _Source: https://taostats.io/docs/new/validators/post-validators-yield_ _Last reviewed: 2026-10-07_ ```http POST https://api.taostats.io/v1/validators/yield ``` Requires an API key in the `Authorization` header. Bulk yield for specific `(hotkey, netuid)` positions. ## Code samples **cURL** ```bash curl -X POST \ -H "Authorization: " \ -H "Content-Type: application/json" \ -d '{ "positions": [ { "hotkey": "string", "netuid": 0 } ] }' \ "https://api.taostats.io/v1/validators/yield" ``` **JavaScript** ```js const response = await fetch('https://api.taostats.io/v1/validators/yield', { method: 'POST', headers: { Authorization: '', 'Content-Type': 'application/json', }, body: JSON.stringify({ "positions": [ { "hotkey": "string", "netuid": 0 } ] }), }); const data = await response.json(); ``` **Python** ```python import requests response = requests.post( "https://api.taostats.io/v1/validators/yield", headers={"Authorization": ""}, json={ "positions": [ { "hotkey": "string", "netuid": 0 } ] }, ) data = response.json() ``` ## Parameters _No parameters._ ## Request body | Field | Type | Required | Description | | --- | --- | --- | --- | | `limit` | `integer (int32), nullable` | | Results per page. Default: 50. Max: 200. | | `min_stake` | `integer (int64), nullable` | | Minimum stake filter (u64 rao). | | `order_by` | `string, nullable` | | Column to order by. Default: `stake`. One of `stake`, `netuid`, `name`, `one_hour_apy`, `one_day_apy`, `seven_day_apy`, `thirty_day_apy`. | | `order_dir` | `string, nullable` | | Sort direction. Default: `desc`. One of `asc`, `desc`. | | `page` | `integer (int32), nullable` | | Page number (1-indexed). Default: 1. Bounded window: `(page − 1) × limit` must be below 1,000,000. Deep `OFFSET` paging is linear in the offset, so an uncapped `page` lets one request scan tens of millions of rows; a page past the window is rejected with a 400. Reach deeper history with this endpoint's range filters (`block_start`/`block_end`, `timestamp_start`/`timestamp_end`) rather than a larger `page`. | | `positions` | `array` | Yes | `(hotkey, netuid)` positions to fetch yield for. Required, non-empty. | | `positions[].hotkey` | `string` | Yes | Validator hotkey (SS58 or 0x-hex). | | `positions[].netuid` | `integer (int32)` | Yes | Subnet ID. | ```json { "positions": [ { "hotkey": "string", "netuid": 0 } ] } ``` ## Responses ### `200` — Paginated validator yield | Field | Type | Required | Description | | --- | --- | --- | --- | | `data` | `array` | Yes | | | `data[].block_number` | `integer (int32)` | Yes | | | `data[].hotkey` | `string` | Yes | Validator hotkey (SS58). | | `data[].name` | `string, nullable` | Yes | On-chain account identity display name (from `IdentitiesV2`, joined by coldkey). `null` when the coldkey has no identity set. | | `data[].netuid` | `integer (int32)` | Yes | | | `data[].one_day_apy` | `number (double)` | Yes | | | `data[].one_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].one_hour_apy` | `number (double)` | Yes | | | `data[].seven_day_apy` | `number (double)` | Yes | | | `data[].seven_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].stake` | `string` | Yes | Stake (`total_hotkey_alpha`), u64 rao as a decimal string. | | `data[].thirty_day_apy` | `number (double)` | Yes | | | `data[].thirty_day_epoch_participation` | `number (double), nullable` | Yes | | | `data[].timestamp` | `string` | Yes | ISO 8601 snapshot timestamp. | | `pagination` | `object` | Yes | Pagination metadata appended to collection responses. `next_page` / `prev_page` are explicitly required+nullable in the OpenAPI spec: per `docs/api_standards.md` we emit `null` rather than omitting the field, so the SDK should model them as `Option`/nullable types that are always present in the response. **`total_items` is the exact number of matching rows** on every route (issue #992). It used to stop at 1,000,000, which reported a million transfers when finney holds 7,513,252 and a million events when the real figure is 981,179,110; the cap was removed on 29 Sep 2026 and nothing may reintroduce one — `crate::pagination::exact_count_sql` is the only count form, and `no_capped_count_reaches_the_api_surface` in `data-debug-scripts` fails the build if a capped one comes back. **`total_pages` is still bounded**, to `crate::pagination::max_reachable_page(per_page)`, because the `page` parameter is capped to a 1,000,000-row window: deep `OFFSET` paging reads every skipped row (24.7M rows / 36.6 GiB / 42 s measured on one endpoint, issue #413). So `total_pages` is exactly the last page this API will serve and `next_page` / `prev_page` stay coherent at the window edge, while `total_items` tells the caller how many rows there really are. History beyond the window is reached with each endpoint's range filters, not a larger `page`. | | `pagination.current_page` | `integer (int32)` | Yes | 1-indexed page that produced the rows in `data`. | | `pagination.next_page` | `integer (int32), nullable` | Yes | Next page number, or `null` if this is the last page. | | `pagination.per_page` | `integer (int32)` | Yes | Number of items requested per page. | | `pagination.prev_page` | `integer (int32), nullable` | Yes | Previous page number, or `null` if this is the first page. | | `pagination.total_items` | `integer (int64)` | Yes | Number of items matching the query across all pages. This is the exact count — every matching row, however many there are. It is not bounded by `total_pages`, so a listing can legitimately report far more items than `total_pages × per_page`. | | `pagination.total_pages` | `integer (int32)` | Yes | Number of pages at the current `per_page` that this API will serve: `ceil(total_items / per_page)`, capped at `ceil(1000000 / per_page)` because the `page` parameter only reaches into the first 1,000,000 rows. When the cap bites, `total_items` is still the true total and this is the last page a request can ask for; reach deeper history with the endpoint's range filters instead of a larger `page`. | ### `400` — Invalid parameter or malformed JSON body | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `413` — Request body exceeds the size limit | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `415` — Content type is not application/json | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | ### `500` — Internal server error | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | `string` | Yes | Plain-english message describing the failure from the caller's perspective. | | `status_code` | `integer (int32)` | Yes | HTTP status code (mirrored in the response status line for convenience). | Every request needs an `Authorization` header holding your API key — see [Getting started with the Taostats API](https://taostats.io/docs/start-here/getting-started-with-taostats-api).