eth_getBlockByNumber: Read a Block by Number or Tag

eth_getBlockByNumberRead methodEthereum JSON-RPC

eth_getBlockByNumber returns a block: every header field plus its transaction list, either as bare hashes or as full transaction objects. It is what block explorers, chain-following indexers and every "what is the current base fee" lookup are built on, and the boolean second parameter is the difference between a small response and a very large one.

1 credit
per call on BLAZED.sh
Read
call type
2
parameters
All clients
client support

Try eth_getBlockByNumber

Request as curl
curl -s https://ethereum-rpc.publicnode.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getBlockByNumber",
  "params": [
    "0x18789ac",
    false
  ]
}'

Runs the example straight from your browser against the endpoint above. The prefilled URL is a third-party public endpoint, not run by BLAZED.sh.

What eth_getBlockByNumber does

The first parameter selects the block, either as a hex block number or as a tag. latest is the node's current head and can still be reorged away; safe and finalized come from the consensus layer, with finalized trailing the head by roughly two epochs and being, for practical purposes, permanent; earliest is genesis; pending is the node's local view of the block it would build next.

The second parameter decides how transactions are rendered. false returns an array of transaction hashes, true replaces it with full transaction objects. Everything else in the response is identical, which makes false the right default for anything that only needs header data.

The header itself has accumulated fields with every fork. Post-Merge, difficulty is 0, nonce is zero, sha3Uncles is the empty-list hash and miner is simply the fee recipient the builder chose. London added baseFeePerGas, Shanghai added withdrawals and withdrawalsRoot, Cancun added blobGasUsed, excessBlobGas and parentBeaconBlockRoot, and Pectra added requestsHash. Fields also disappear: recent Geth no longer returns totalDifficulty at all.

Parameters

#NameTypeDescription
1blockParameterstringHex block number (minimal form, e.g. 0x18789ac) or one of latest, safe, finalized, earliest, pending.
2fullTransactionsbooleanfalse returns the transactions array as hashes; true returns full transaction objects, which multiplies the response size.

What it returns

A block object, or null when the node does not know that block: number, hash, parentHash, nonce, sha3Uncles, logsBloom, transactionsRoot, stateRoot, receiptsRoot, miner, difficulty, extraData, size, gasLimit, gasUsed, timestamp, transactions, uncles, plus the fork-specific fields baseFeePerGas, withdrawals, withdrawalsRoot, blobGasUsed, excessBlobGas, parentBeaconBlockRoot and requestsHash.

All quantities are hex. timestamp is unix seconds, not milliseconds. size is the RLP-encoded size of the block in bytes and has nothing to do with the size of the JSON you receive, which is several times larger when full transaction bodies are included.

Example response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x18789ac",
    "hash": "0x7e9ea4877f25847c29ea338043948a8ea43383da1620ace955a20baf667c5ff5",
    "parentHash": "0x161ab6d812fb274e0d07f71f197b458be7f96451932b129af3001487d7829a0c",
    "timestamp": "0x6a6ddccb",
    "gasLimit": "0x3938700",
    "gasUsed": "0x1e513f7",
    "baseFeePerGas": "0x2e0a427",
    "blobGasUsed": "0x20000",
    "excessBlobGas": "0xa6d7655",
    "difficulty": "0x0",
    "miner": "0x396343362be2a4da1ce0c1c210945346fb82aa49",
    "size": "0x21afc",
    "transactions": [
      "0x20f83f863231a8e1c97f14d1b2e9379d389c57d3bb82d265cd6e5d806378ba9b",
      "0x2efde06bb1dcf26741f3a7c1385d5a62ad2554523322e9dd53f1635ba6add6d1"
    ],
    "uncles": []
  }
}

Trimmed: the real response also carries logsBloom, the three tries' roots, extraData, mixHash, the withdrawals list and parentBeaconBlockRoot. Block 0x18789ac held 209 transactions; as hashes the whole response is roughly 18KB, and the same block requested with full bodies is roughly 377KB.

eth_getBlockByNumber with ethers.js

import { JsonRpcProvider, formatUnits } from "ethers";

const provider = new JsonRpcProvider("https://ethereum-rpc.publicnode.com");

// hashes only: eth_getBlockByNumber(tag, false)
const block = await provider.getBlock("latest");
console.log(block.number, block.transactions.length, "txs");
console.log("base fee:", formatUnits(block.baseFeePerGas, "gwei"), "gwei");

// prefetch = true asks for full bodies: a much larger response
const full = await provider.getBlock("latest", true);
for (const tx of full.prefetchedTransactions.slice(0, 3)) {
  console.log(tx.hash, tx.from, "->", tx.to);
}

Gotchas and common errors

Block numbers are hex quantities

The block parameter is a hex quantity in minimal form: 0x18789ac, not 25659820, not "25659820" and not 0x018789ac. Passing a decimal string is the single most common cause of "invalid argument 0" on this method. Libraries convert for you; hand-written curl and shell scripts are where it bites.

latest, safe and finalized are three different questions

latest is whatever the node currently believes is the head, and it can be reorged out from under you. safe is the consensus layer's justified checkpoint and finalized is its finalized one, roughly two epochs behind the head. Anything that writes to a database and cannot easily roll back should read finalized, or read latest and keep a confirmation buffer.

true is the expensive flag

Passing false and then fetching the handful of transactions you actually care about is almost always cheaper than pulling every body. The block above is roughly 18KB with hashes and roughly 377KB with bodies, so the flag decides whether the response lands under or well over the 100KB threshold where the per-MB surcharge starts. Set it to true only when you genuinely need every transaction in the block, as a block-by-block indexer does.

pending is node-local and unstable

The pending block is a projection of what this particular node would build next from its own mempool, so two nodes return different pending blocks at the same instant and the content shifts between calls. It is useful for local fee estimation and useless for anything that must reconcile across machines. Some clients and hosted endpoints return null for it outright.

difficulty and totalDifficulty are dead weight

Post-Merge, difficulty is always 0 and totalDifficulty is frozen at the terminal value; recent Geth stopped returning totalDifficulty from block responses entirely. Code that still uses either field to detect the chain or measure progress silently breaks. Use block numbers, and the finalized tag for settlement.

A number is not an identity

Across a reorg, the same block number points at different blocks. If you fetch a block by number and then fetch receipts or logs from it, carry the blockHash through and check it, or fetch by hash with eth_getBlockByHash in the first place. Indexers that key on block number alone are the ones that silently corrupt themselves during reorgs.

What eth_getBlockByNumber costs on BLAZED.sh

eth_getBlockByNumber costs 1 credit per call on BLAZED.sh whichever way the boolean is set, but the response-size surcharge does not care about that: responses over 100KB add 50 credits per MB. Header-only requests are a few tens of kilobytes and never come close; full-body requests on a busy mainnet block routinely serialize past 300KB and do. A block-follower that only needs the head number, base fee and timestamp should always pass false.

See the full credit price list

eth_getBlockByNumber: frequently asked questions

What is the difference between eth_getBlockByNumber and eth_getBlockByHash?

They return the same object; only the lookup key differs. Block numbers are ambiguous across reorgs because the same height can hold different blocks over time, while a block hash identifies exactly one block forever. Follow the chain by number, then pin follow-up requests by hash.

How do I get the current base fee?

Read baseFeePerGas from the latest block. For a series rather than a single value, eth_feeHistory returns the recent base fees together with the next block's projected base fee in one call.

Why does eth_getBlockByNumber return null?

The node does not have that block: it is above the node's head, the node is still syncing, or you asked for pending on a client that does not build one. Check eth_blockNumber and eth_syncing before assuming the block does not exist.

Does passing true cost more credits?

The call itself is still 1 credit, but full transaction bodies push mainnet blocks well past the 100KB threshold where responses add 50 credits per MB. Pass false unless you need every transaction.

Is the timestamp in seconds or milliseconds?

Seconds, hex encoded. Multiply by 1000 before handing it to JavaScript's Date. Post-Merge slots are 12 seconds apart, so consecutive block timestamps normally differ by 12 or a multiple of 12 when slots are missed.

Call eth_getBlockByNumber from the node itself

Deploy your container or script next to a synced Ethereum node. No rate limits, no compute units, sub-10ms local RPC.