getLogs best practices

eth_getLogs can be an expensive read. Narrow filters and bounded ranges keep it reliable; an empty result only means no matching logs were returned for that filter and range.

Chunk your block ranges

jsjs
// paginate a large range in fixed-size chunks
const CHUNK = 2000n
for (let from = start; from <= end; from += CHUNK) {
const to = from + CHUNK - 1n > end ? end : from + CHUNK - 1n
const logs = await client.request({
method: 'eth_getLogs',
params: [{
fromBlock: '0x' + from.toString(16),
toBlock: '0x' + to.toString(16),
address: contractAddress,
topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'],
}],
})
// process logs…
}
  • Set contractAddress to a contract deployed on the selected network and configure client, start and end before using the example.
  • Filter by known addresses and topics. For non-anonymous events, topic 0 is the event signature hash; anonymous events omit that signature topic.
  • The 2,000-block chunk is only a starting point. On range, payload or timeout errors, shrink it and retry; upstream limits vary.
  • Where supported, use finalized as a conservative boundary under that chain's finality assumptions. A block number is a height, not a promise that its hash can never change.
  • Save progress only after a chunk is processed successfully. Track block hashes, handle reorgs and make repeated processing idempotent.
  • For continuous tailing, poll bounded ranges on HTTP-only networks. A WebSocket-enabled network can use logs subscriptions with HTTP backfill.