state_getKeysPaged - Polkadot RPC Method
Enumerate storage keys with pagination on Polkadot. Iterate over storage maps like accounts, validators, and assets efficiently with cursor-based pagination.
Returns storage keys matching a prefix with cursor-based pagination on Polkadot. This is the standard way to iterate over storage maps (like System.Account, Staking.Validators, or any pallet storage map) without loading all keys into memory at once.
Why Polkadot? Build on the heterogeneous multi-chain protocol with $300M+ DeFi TVL and JAM supercomputer upgrade incoming with shared relay chain security, XCM cross-chain messaging, JAM upgrade targeting 1M TPS, and 2.1B DOT supply cap.
When to Use This Method
state_getKeysPaged is essential for parachain developers, cross-chain dApp builders, and teams requiring shared security:
- Storage Map Iteration -- Enumerate all entries in a storage map (accounts, balances, staking data) on Polkadot
- Data Export and Indexing -- Bulk export on-chain state for analytics, indexers, and data pipelines for cross-chain DeFi, parachain deployment, XCM interoperability, and DOT staking (8-10% APY)
- Account Enumeration -- List all accounts that have balances, staking positions, or other on-chain state
- State Migration Tooling -- Iterate storage for runtime upgrades, audits, or cross-chain migration
Code Examples
Common Use Cases
1. Enumerate All Accounts
List all accounts with on-chain state and fetch their balances:
import { ApiPromise, WsProvider } from '@polkadot/api';
async function enumerateAccounts(api, pageSize = 200) {
const prefix = api.query.system.account.keyPrefix();
const allKeys = [];
let startKey;
// Paginate through all account keys
while (true) {
const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
if (keys.length === 0) break;
allKeys.push(...keys);
startKey = keys[keys.length - 1];
}
console.log(`Found ${allKeys.length} accounts`);
// Fetch balances in batches using queryStorageAt
const batchSize = 100;
for (let i = 0; i < allKeys.length; i += batchSize) {
const batch = allKeys.slice(i, i + batchSize);
const results = await api.rpc.state.queryStorageAt(batch);
results[0].changes.forEach(([key, value]) => {
if (value) {
const accountInfo = api.createType('AccountInfo', value);
console.log(` Free: ${accountInfo.data.free.toHuman()}`);
}
});
}
}2. Export Storage Map for Analysis
Export all entries of a specific storage map for offline analysis:
async function exportStorageMap(api, palletName, storageName) {
const prefix = api.query[palletName][storageName].keyPrefix();
const entries = [];
let startKey;
while (true) {
const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
if (keys.length === 0) break;
const values = await api.rpc.state.queryStorageAt(keys);
for (const [key, value] of values[0].changes) {
entries.push({
key: key.toHex(),
value: value ? value.toHex() : null
});
}
startKey = keys[keys.length - 1];
console.log(`Exported ${entries.length} entries...`);
}
return entries;
}
// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');3. Count Storage Items by Prefix
Get a count of entries in any storage map without fetching values:
async function countStorageKeys(api, prefix) {
let count = 0;
let startKey;
while (true) {
const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
if (keys.length === 0) break;
count += keys.length;
startKey = keys[keys.length - 1];
}
return count;
}
// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);Error Handling
Common errors and solutions:
| Error Code | Description | Solution |
|---|---|---|
| -32603 | Internal error | Verify the prefix is a valid hex string; reduce count if the request is too large |
| -32602 | Invalid params | Ensure prefix and startKey are properly hex-encoded with 0x prefix |
| -32005 | Rate limit exceeded | Reduce page size or add delays between pagination requests |
| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
| Timeout | Response too slow | Reduce count parameter (try 100 instead of 1000) |
Related Methods
state_getStorage-- Get the value for a specific storage keystate_queryStorageAt-- Batch query multiple storage keys at oncestate_call-- Call a runtime API functionstate_getMetadata-- Get runtime metadata to determine storage key prefixes
state_call
Call a runtime API function on Polkadot. Execute on-chain computations like account nonce lookups, fee estimation, and custom runtime logic without submitting a transaction.
state_queryStorageAt
Batch query multiple storage keys at a specific block on Polkadot. Efficiently retrieve consistent multi-key state snapshots for indexers, dashboards, and analytics.