Reading raw blockchain data · Lesson 5 of 6
Node APIs and data access
8 min read
Every explorer, dataset and analytics platform ultimately reads the chain through a node's API. Knowing those APIs lets you fetch exactly the data a finding depends on, verify what a third-party tool shows you, and understand why a query that works for a recent block fails for an old one. This lesson covers the methods analysts use most, the node types they require and the practical limits of providers and public datasets.
Two families of APIs
Bitcoin Core exposes a JSON-RPC interface: an HTTP POST with a method name and a params array, authenticated with a cookie file or a configured user. The same methods are available on the command line through bitcoin-cli. Amounts are returned in BTC as JSON numbers.
Ethereum execution clients implement a common JSON-RPC specification maintained by the Ethereum community, so the core eth_ methods behave the same on Geth, Nethermind, Besu, Erigon and Reth. Most EVM chains, including rollups and sidechains, offer the same interface. Additional namespaces such as debug_ and trace_ are client-specific. Quantities are returned as hexadecimal strings ("0x1b4"), and block arguments accept either a hex number or a tag (latest, safe, finalized, earliest, pending), as the lesson Blocks and headers as data explains.
Bitcoin Core methods analysts use
| Method | Returns | Notes |
|---|---|---|
getblockchaininfo | Chain, height, best block hash, pruning status | First call in any session |
getblockhash height | Hash of the main-chain block at that height | Can change near the tip |
getblockheader hash | Header fields and confirmations | Light and fast |
getblock hash verbosity | 0: hex; 1: header and txids; 2: decoded transactions with fees; 3: adds prevout for each input | Fees and prevouts need undo data |
getrawtransaction txid verbosity | 0: hex; 1: decoded; 2: adds fee and prevouts | Confirmed transactions need txindex=1 or a block hash |
decoderawtransaction hex | Decoded structure of a hex transaction | No prevouts, so no fee |
gettxout txid n | Output details if currently unspent | Null if spent or unknown |
gettxspendingprevout | Transaction spending given outputs | Mempool only, unless txospenderindex is enabled (31.0 and later) |
getchaintips | Known branches and their status | Reorg investigation |
A typical sequence for examining a payment looks like this:
getblockhash 880214 -> "0000…3f1a"
getblock "0000…3f1a" 1 -> header fields and the list of txids
getrawtransaction "4f3a…c21e" 2 -> decoded transaction with prevout values and fee
gettxout "4f3a…c21e" 1 -> output 1 if still unspent, otherwise null
The last call checks only the current UTXO set: it tells you whether an output is unspent now, not when or by what it was spent. Finding the spender has traditionally required an indexer or a warehouse; since version 31.0, Bitcoin Core offers an optional spender index (txospenderindex) that lets gettxspendingprevout return the confirmed transaction that spent an output. Bitcoin Core still has no method that lists an address's history; that question needs an address indexer or a warehouse.
Ethereum JSON-RPC methods
| Method | Returns |
|---|---|
eth_chainId | The chain ID (EIP-695); confirms which network you are on |
eth_blockNumber | Height of the latest block |
eth_getBlockByNumber, eth_getBlockByHash | Header and transaction hashes, or full transactions when the second parameter is true |
eth_getTransactionByHash | The signed transaction with from recovered |
eth_getTransactionReceipt | Status, gas, logs |
eth_getBlockReceipts | All receipts of a block in one call; widely implemented by clients but missing from older documentation |
eth_getLogs | Logs matching a filter |
eth_getBalance, eth_getCode, eth_getStorageAt, eth_call | State at a given block |
Two behaviors catch newcomers. eth_getTransactionReceipt returns null for a transaction that is pending or unknown to the node, which is not the same as a failure. And the state methods take a block argument: eth_getBalance at latest gives a different answer every few seconds, so pass an explicit block number for any figure you intend to report.
eth_getLogs is the workhorse for token and protocol analysis. Its filter takes fromBlock and toBlock (or a single blockHash instead), an optional address and a positional topics array. This request asks a token contract for Transfer events sent to one address over 1,000 blocks:
{"jsonrpc": "2.0", "id": 1, "method": "eth_getLogs",
"params": [{
"fromBlock": "0x1406f40",
"toBlock": "0x1407327",
"address": "0x5d3a…7c10",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000c3d4…22e1"]
}]}
The blocks run from 21,000,000 (0x1406f40) to 21,000,999 (0x1407327). Topics match by position: the first is the Transfer signature, null accepts any sender, and the third, topics[2], fixes the recipient. A position may also hold an array, meaning "any of these." Moving the padded address to the second position would return transfers sent by it instead.
Debug and trace methods
Internal calls require re-execution. Geth-style clients offer debug_traceTransaction, debug_traceBlockByNumber and debug_traceCall, with a tracer option such as callTracer (the call tree) or prestateTracer (the state a transaction touched). Parity-style clients such as Erigon, Nethermind and Reth also offer trace_transaction, trace_block, trace_filter and trace_replayTransaction, which return flat call lists.
These methods are expensive: a node replays every opcode of the transaction, and for old blocks it must first rebuild the historical state. Providers often disable them, limit them or charge more. The two styles also differ in shape and in details such as whether calls to precompiles appear, so normalize them before mixing results from different clients.
Which questions need which node
| Question | Bitcoin | Ethereum |
|---|---|---|
| Blocks, transactions, receipts, logs | Full node (txindex=1 for lookup by txid) | Full node; pre-Merge history may be expired |
| Current balance or UTXO status | gettxout, or an indexer for addresses | Full node, eth_getBalance at latest |
| Balance at a past block | Indexer or warehouse | Archive node |
| Address history | Address indexer or warehouse | Indexer or warehouse (no native method) |
| Internal calls of old transactions | Not applicable | Archive node with tracing |
Pruned nodes of either chain cannot serve old blocks at all, and an Ethereum full node asked for state older than its retained window returns an error rather than a wrong number. That error is a useful signal that you need an archive node.
Rate limits, pagination and provider trust
Hosted providers protect their nodes with rate limits, counted in requests per second or in weighted "compute units," and with caps on eth_getLogs: a maximum block range, a maximum number of results, or both. Robust extraction code splits a range into chunks, halves a chunk when the provider rejects it, retries with exponential backoff and records which ranges have completed. Many providers accept JSON-RPC batch requests (an array of calls in one HTTP request), which reduces overhead but counts against the same limits.
Providers also raise questions of trust:
- Inconsistent heads. Behind a load balancer, consecutive calls can reach nodes at different heights, so a block returned by one call may be "not found" by the next. Pin work to a block hash rather than to
latest. - Errors and lag. A provider's node can be out of sync, misconfigured or serving a different client version. For facts a finding depends on, compare the block hash at your anchor height across two independent sources or check against your own node. Record the client version with
web3_clientVersionon Ethereum or thesubversionfield ofgetnetworkinfoon Bitcoin Core. - Query privacy. A provider sees every address and transaction you ask about. For sensitive investigations, a provider's logs reveal the subject of your interest; your own node does not.
Public datasets and ETL frameworks
For bulk history, extracting everything through RPC is slow. Two alternatives exist.
Public datasets hosted by major cloud providers hold Bitcoin, Ethereum and other chains already loaded into tables: for example the crypto datasets in Google BigQuery's public data program, produced by the open-source blockchain-etl project, and the AWS Public Blockchain Data set, published as Parquet files. They give SQL access to complete history in minutes. In exchange you accept their schema, their decoding choices, their update lag and their reorg policy, all of which you should read before relying on a table.
ETL frameworks let you build your own tables from a node: Bitcoin ETL and Ethereum ETL (blockchain-etl), cryo (which extracts blocks, transactions, logs and traces to Parquet or CSV), TrueBlocks (which indexes every appearance of an address) and The Graph (which indexes events for specific protocols through subgraphs). Running your own pipeline costs effort but gives you control over the node, the decoding and the provenance of every row. The lesson The analytics data stack compares these options.
Exam focus: Bitcoin Core'sgetblockandgetrawtransactionverbosity levels and thetxindexrequirement; thatgettxoutchecks only the current UTXO set and that Bitcoin Core has no address history method; the core Ethereum methods includingeth_getBlockByNumber,eth_getTransactionReceipt,eth_getBlockReceiptsandeth_chainId; theeth_getLogsfilter and positional topic matching withnullwildcards; Geth-styledebug_trace*versus Parity-styletrace_*methods; which questions need an archive node, and handling rate limits and range caps; verifying providers and recording client versions; public datasets and ETL frameworks and their trade-offs.