eth_getBlockReceipts: Every Receipt in a Block, One Call
If you're indexing a chain, you often don't want one transaction receipt — you want all of them for a block: every status, every log, every gas number, in order. The obvious approach is to list the block's transactions and call eth_getTransactionReceipt for each. That's the classic N+1 problem: a busy Ethereum block has 200+ transactions, so one block becomes 200+ RPC round-trips. eth_getBlockReceipts collapses that into a single call.
The method
eth_getBlockReceipts takes a block (number, tag, or hash) and returns an array of receipt objects — the same shape eth_getTransactionReceipt returns, one per transaction, in block order:
curl -s -X POST https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockReceipts","params":["latest"],"id":1}'
# -> {"result":[ {receipt for tx 0}, {receipt for tx 1}, … ]}
The block argument accepts what you'd expect: a hex block number ("0xf4240"), a tag ("latest", "finalized", "safe", "earliest"), or a block hash. Each element carries the full receipt — status, logs, gasUsed, cumulativeGasUsed, effectiveGasPrice, contractAddress, and the rest (see Reading a Transaction Receipt for what every field means).
Why it matters: the round-trip math
The value is entirely about round-trips and rate limits. Compare fetching receipts for a 200-transaction block:
| Approach | RPC calls | HTTP requests |
|---|---|---|
eth_getTransactionReceipt per tx |
200 | 200 (or 1 if batched) |
eth_getBlockReceipts |
1 | 1 |
At 200 calls per block, an indexer walking even a few thousand blocks is making hundreds of thousands of requests — burning your rate limit, multiplying latency by network round-trip time, and hammering the node. One call per block turns that into a rounding error. For anything block-oriented — explorers, analytics, log indexers, accounting — this is the method you want.
In your library
Most libraries don't wrap it as a first-class action yet, so the raw request form is the reliable path:
// viem — raw request (works regardless of action coverage)
const receipts = await publicClient.request({
method: "eth_getBlockReceipts",
params: ["latest"],
});
// ethers v6
const receipts = await provider.send("eth_getBlockReceipts", ["latest"]);
# web3.py (v6.5+ exposes it directly)
receipts = w3.eth.get_block_receipts("latest")
# older versions: w3.provider.make_request("eth_getBlockReceipts", ["latest"])
From there you have the whole block's receipts in memory — sum gasUsed × effectiveGasPrice for total fees paid, flatten every logs array to index all events, or filter by status to find the block's reverted transactions.
eth_getBlockReceipts vs JSON-RPC batching
There are two ways to avoid N separate HTTP requests, and they're not the same thing:
eth_getBlockReceipts— a single RPC method that returns a whole block's receipts. One method, one response. Cleanest when you want exactly "this block's receipts."- JSON-RPC batching — packing N
eth_getTransactionReceiptcalls into one HTTP request. Still N logical calls (and N results to match up), but one round-trip.
Rule of thumb: for a whole block, prefer eth_getBlockReceipts — it's one logical call and the node fetches receipts together efficiently. Reach for batching when you need an arbitrary set of receipts (specific txids scattered across blocks) rather than a contiguous block, since getBlockReceipts only works block-at-a-time. The two compose well: getBlockReceipts for backfilling block ranges, batched getTransactionReceipt for targeted lookups.
Availability and fallback
eth_getBlockReceipts is supported by modern execution clients — Geth (1.13+), Erigon, Nethermind, and Reth — and by the major RPC providers. But it's newer than eth_getTransactionReceipt, so an older node or a minimal endpoint may not expose it. Defensive indexers detect that (a -32601 "method not found") and fall back to a batch:
async function blockReceipts(client, block) {
try {
return await client.request({ method: "eth_getBlockReceipts", params: [block] });
} catch (e) {
// Method not found -> fall back to a batch of per-tx receipts
const { transactions } = await client.request({
method: "eth_getBlockByNumber", params: [block, false],
});
return client.request( // batched
transactions.map((tx, id) => ({
method: "eth_getTransactionReceipt", params: [tx], id,
})),
);
}
}
That way the fast path is used where available and you degrade gracefully everywhere else.
Where it shines
- Indexing and explorers — pull every receipt (and every log) for a block in one call as you walk the chain.
- Block-level accounting — total fees, total gas, count of reverted transactions, all from one response.
- Reorg re-indexing — when a block is reorged out and a new one takes its place, re-fetching the replacement block's receipts is a single call, not a fresh fan-out.
- Log pipelines — flatten the receipts'
logsarrays for a complete, ordered event stream for the block (complementary to a liveeth_subscribefeed).
The short version
eth_getBlockReceipts returns every receipt in a block in one call — same receipt shape as eth_getTransactionReceipt, one per transaction, in order — turning the N+1 round-trip problem into a single request. Use it for anything block-oriented (indexing, explorers, fee accounting, reorg re-index); use JSON-RPC batching when you need an arbitrary scatter of receipts instead of a whole block; and detect method not found to fall back to a batched per-tx fetch on nodes that don't support it.
Block-range indexing is only as fast as your endpoint lets it be. A flat-rate Ethereum RPC endpoint exposes eth_getBlockReceipts (and batching, and the full receipt surface) across dozens of chains under one key. Grab a free key and point your app at:
https://rpc.swiftnodes.io/rpc/eth?key=YOUR_API_KEY
Related posts
- How `eth_getLogs` Range Caps Bite You in Production
Your indexer works fine until it hits a single dense block range, then everything stops. Here's the cap mechanics across providers, the adaptive chunking pattern that actually works, and the code to do it right.
- Did It Actually Succeed? Reading eth_getTransactionReceipt
A transaction being mined does not mean it worked — a reverted transaction still lands in a block and still costs you gas. The receipt is where the truth lives: the status flag, the event logs, the real gas paid, and how many confirmations you should wait. Here's how to read one.
- Estimating Gas Right: eth_estimateGas, EIP-1559, and Buffers
Half of failed transactions are gas mistakes: an 'out of gas' revert or a fee too low to ever get mined. Here's how eth_estimateGas actually works, how EIP-1559 fees are built, why you buffer the gas limit, and how to put it all together without overpaying.