7 minIteradian Engineeringperformancepricing

Why eth_getLogs is the call that breaks your RPC budget

One method accounts for a disproportionate share of most teams RPC spend and most of their timeouts. This explains what the node is actually doing during an eth_getLogs call, why block range is the variable that matters, and the query patterns that keep it cheap.

If you look at a month of RPC usage grouped by method, the distribution is almost never even. A handful of methods carry the volume, and one of them — eth_getLogs — usually carries the cost and the timeouts out of all proportion to its call count. Understanding why makes the fix obvious.

What the node is doing

A call like eth_blockNumber is a lookup. The node knows the answer and returns it. eth_getLogs is a scan: the node walks the block range you asked for, checks each block bloom filter against your address and topic filters, and for every candidate block reads the receipts and filters the logs inside them.

The cost therefore scales with the block range, not with the number of results. A query that scans 500,000 blocks and returns nothing is far more expensive than one that scans 100 blocks and returns a thousand entries. This is the single most common misunderstanding about the method, and it is why "my query returns almost no data, why is it slow" is such a frequent question.

The variables that matter, in order

  1. Block range. The dominant term. Halving the range roughly halves the work.
  2. Filter selectivity. An address filter lets the bloom filter reject most blocks cheaply. No address filter means reading far more receipts.
  3. Result size. Matters for serialisation and transfer, but usually second to range.
  4. Chain history depth. The same range costs more on a chain with larger blocks.

Patterns that keep it cheap

Page by block range, not by result count

Pick a fixed range per request — 2,000 blocks is a common starting point — and walk forward. Ranges keep the per-request cost predictable, which matters when you are working against an enforced ceiling. Result-count paging does not, because you cannot know the count without doing the scan.

Always filter by address when you can

json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "0x1312D00",
    "toBlock": "0x13135E0",
    "address": "0x...",
    "topics": ["0xddf252ad..."]
  }],
  "id": 1
}

The address turns a full receipt read into a bloom filter check for most blocks in the range. Topics narrow it further. A query with neither is asking the node to read everything.

Back off on failure rather than retrying identically

If a range times out, retrying the same range will time out again. Halve it. A backoff that narrows the range converges; one that only waits longer does not.

How this interacts with billing

This is where the metering unit decides your bill. Under per-request billing, a 2,000-block scan and a balance read cost the same — one request each — so your cost is driven by how many calls you make, and range-paging is purely a latency and reliability decision. Under compute-unit or credit billing the weighting is per method, so the same workload can cost many times more. Iteradian meters in plain requests, which is why the advice here is about timeouts rather than about spend.

Either way, the per-method breakdown is the thing to look at first. If you cannot see which method is generating your volume, you cannot fix the one that is.

Frequently asked questions

01

Why is eth_getLogs slow even when it returns no results?

Because the cost scales with the block range scanned, not the number of results returned. The node walks every block in the range and checks its bloom filter, so a wide range that matches nothing is still expensive.

02

What is a safe block range for eth_getLogs?

Start at around 2,000 blocks per request and adjust. A fixed range keeps per-request cost predictable; if a request times out, halve the range rather than retrying the same one.

03

Does filtering by address make eth_getLogs faster?

Yes, substantially. An address filter lets the node reject most blocks with a bloom filter check instead of reading their receipts. Adding topic filters narrows it further.

Keep reading

Start

Point one endpoint at Iteradian and watch where the traffic goes.

Create a key, send a JSON-RPC call, and read that request back in your analytics a second later. No migration, no contract.

Trial
7 days, no card required
Setup
One endpoint, one header
Commitment
Monthly or annual, cancel any time