The Query Stack
Every interaction with Subtensor passes through a stack of abstraction layers. Understanding
these layers helps you choose the right tool, debug problems when they arise, and know what's
happening under the hood when you call api.query.
The 5 Layers
When you query a Subtensor node, your request passes through up to five abstraction layers. Higher layers are easier to use; lower layers give you more control.
state_callstate_getStorage, chain_getBlock,
etc.Layer 1: SDKs & Libraries
SDKs are the highest abstraction layer. They handle SCALE encoding, type resolution, metadata parsing, and connection management so you can write clean, typed code.
| SDK | Language | Best For |
|---|---|---|
| polkadot.js | TypeScript | Web apps, scripting, most common choice |
| bittensor SDK | Python | Miners, validators, data science |
| subxt | Rust | High-performance, type-safe, compiled |
What Happens Under the Hood
When you write api.query.subtensorModule.tempo(1), the SDK performs four steps:
Almost always. Unless you have a specific reason to go lower (custom encoding, unsupported methods, debugging), start with an SDK.
Layer 2: Runtime APIs
Runtime APIs are named functions that execute inside the Wasm runtime. You call them via the state_call JSON-RPC method, which
acts as a bridge between the outer node and the inner runtime.
SDK vs Raw JSON-RPC
The SDK wraps state_call for
you. Here's what it looks like at each level:
const result = await api.call.subtensorModule.getServingRateLimit(netuid);{
"jsonrpc": "2.0",
"id": 1,
"method": "state_call",
"params": [
"SubtensorModuleApi_get_serving_rate_limit",
"0x0100000000000000",
null
]
}Key Subtensor Runtime APIs
Subtensor exposes several runtime API traits. These provide computed values that aren't directly available in storage.
SubtensorModuleApi— subnet parameters, neuron info, staking dataDelegateInfoRuntimeApi— delegate metadata and nominationsNeuronInfoRuntimeApi— neuron details for a subnetSubnetInfoRuntimeApi— subnet metadata and hyperparametersStakeInfoRuntimeApi— staking information by coldkey or hotkey
See the full list in the Runtime APIs Reference.
Layer 3: Pallet Storage & Calls
At this layer you work directly with the pallet's storage items, calls, events, constants,
and errors. The runtime metadata (available via state_getMetadata) describes all of these, including their SCALE types.
| Pallet Item | RPC Method | Direction |
|---|---|---|
| Storage | state_getStorage | Read |
| Calls | author_submitExtrinsic | Write |
| Events | chain_getBlock | Read (from block) |
| Constants | state_getMetadata | Read (from metadata) |
| Errors | — | Returned in dispatch results |
Layer 4: JSON-RPC Methods
JSON-RPC is the wire protocol for communicating with a Substrate node. Every query ultimately becomes a JSON-RPC call. Methods are organized into namespaces.
| Namespace | Purpose | Examples |
|---|---|---|
| state | Query storage, metadata, runtime | state_getStorage, state_call |
| chain | Blocks, headers, finality | chain_getBlock, chain_getHeader |
| system | Node info, health, peers | system_chain, system_health |
| author | Submit extrinsics | author_submitExtrinsic |
| payment | Fee estimation | payment_queryInfo |
Example: Raw curl Request
curl -X POST https://entrypoint-finney.opentensor.ai:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "state_getStorage",
"params": ["0x658faa385070e074c85bf6b568cf055564b..."]
}'See the full list in the JSON-RPC Methods Reference.
Layer 5: Transport
The lowest layer is the network connection itself. Substrate nodes expose two transport options.
| Transport | Protocol | Subscriptions | Best For |
|---|---|---|---|
| HTTP | POST request/response | No | One-off queries, serverless functions |
| WebSocket | Persistent bidirectional | Yes | Real-time apps, event subscriptions |
If you need to subscribe to new blocks, finalized heads, or storage changes, you must
use a WebSocket connection (wss://). HTTP connections only support request/response.
Historical Queries
By default, queries return data from the latest finalized block. To query state at a specific point in time, pass a block hash.
Example: Querying at a Past Block
// Get the block hash for a specific block number
const blockHash = await api.rpc.chain.getBlockHash(4_200_000);
// Query storage at that block
const apiAt = await api.at(blockHash);
const tempo = await apiAt.query.subtensorModule.tempo(1);
// Raw JSON-RPC equivalent: pass blockHash as last param
// { "method": "state_getStorage", "params": ["0x...", blockHash] }Historical queries only work against archive nodes, which store the full state at every block. Standard (pruned) nodes only keep recent state and will return errors for old block hashes. Public endpoints are typically pruned. See Node Operations for more.
JSON-RPC v1 vs v2
Subtensor exposes both legacy v1 methods and several newer Substrate JSON-RPC v2 namespaces. Most SDKs and documentation still use v1, but the v2 API is available for chain head tracking, chain spec queries, and transaction broadcasting.
| v2 Namespace | Status | v1 Equivalent |
|---|---|---|
| chainHead_v1_* | Available | chain_subscribeNewHeads |
| chainSpec_v1_* | Available | system_chain, system_properties |
| transaction_v1_* | Available | author_submitExtrinsic |
| transactionWatch_v1_* | Available | author_submitAndWatchExtrinsic |
| archive_unstable_* | Not Available | state_getStorage + blockHash |
The archive_unstable_* namespace is not present on
Subtensor nodes. For historical state queries, use state_getStorage with a blockHash parameter on an archive node. Always verify
method availability against the supported methods list.
Choosing the Right Layer
Most developers should stay at Layer 1 (SDKs). Here's when you'd reach for a lower layer.
| I want to... | Use This | Layer |
|---|---|---|
| Query storage or submit extrinsics | polkadot.js / bittensor SDK | 1 |
| Call a computed runtime function | Runtime API via SDK | 1-2 |
| Understand available storage items | Runtime metadata | 3 |
| Debug a failing query | curl + JSON-RPC | 4 |
| Build a lightweight HTTP client | Raw HTTP + JSON-RPC | 4-5 |
| Subscribe to live events | WebSocket connection | 5 |
Start at the highest layer that meets your needs. Drop down a layer only when the one above doesn't expose what you need, or when you need to debug what's happening underneath.
Related Resources
Dive deeper into the individual layers.