How to fix eth_getLogs query returned more than 10000 results
If you have built an event indexer on Ethereum, you have hit this wall. You send an eth_getLogs request across a wide block range. The node replies with an error: query returned more than 10000 results. Most RPC providers enforce this cap. The limit protects node performance. It also protects you from accidentally requesting a dataset that would time out or crash your client.
The cap is not a bug. It is a design constraint. You work around it by splitting your query into smaller pieces. This page shows you how.
Why the limit exists
Ethereum nodes store logs in memory-mapped databases. Scanning millions of logs in a single call strains the node. Providers like Infura and Alchemy set the 10000-result ceiling per request. Many also limit the block range width - typically 10000 blocks for archive nodes, less for full nodes. Exceed either limit and the call fails.
The error message varies. Some nodes return "query returned more than 10000 results." Others return a generic "limit exceeded" error. The fix is the same regardless.
The chunking pattern
Chunking means dividing a large block range into smaller ranges. Each chunk must produce fewer than 10000 logs. You query each chunk separately. Then you merge the results.
Here is a concrete approach in pseudocode:
function getLogsInRange(fromBlock, toBlock, address, topics):
allLogs = []
chunkSize = 2000 // blocks per chunk
current = fromBlock
while current <= toBlock:
end = min(current + chunkSize - 1, toBlock)
logs = eth_getLogs(fromBlock=current, toBlock=end, address, topics)
allLogs.append(logs)
current = end + 1
return allLogs
The chunk size depends on how many logs your contracts emit. A busy DEX may produce 5000 logs per 1000 blocks. A quiet token may produce 10. Start with 2000 blocks per chunk. If you still hit the 10000 limit, shrink the chunk size. If you see empty results, you can grow it.
You can parallelise chunked requests. Send all chunks at once if your provider allows concurrent calls. Watch for rate limits. The page on fixing 429 Too Many Requests covers that separately.
Pagination via block range
Some developers ask about offset-based pagination. eth_getLogs does not support an offset parameter. You cannot skip the first 10000 logs and fetch the next 10000. You must paginate by block range.
If you know the block where the first 10000 logs end, start the next query from that block plus one. You can find the boundary by binary searching the block range. This is more efficient than fixed chunking when you need precise boundaries.
The "filter not found" error
When you use eth_newFilter and eth_getFilterChanges, the node creates a filter object. Filters expire after a timeout - typically 5 to 10 minutes on most nodes. If you call eth_getFilterChanges after the filter expires, you get "filter not found."
This error is common in long-running indexers that poll infrequently. The fix is to recreate the filter before it expires. Track the filter ID and the time you created it. If the filter is older than the node's timeout, call eth_newFilter again before calling eth_getFilterChanges.
Some developers avoid filters entirely. They use eth_getLogs with a block range that advances each poll cycle. This trades filter expiry for manual block tracking.
Real-time alternatives
For real-time log streaming, switch to WebSocket subscriptions. eth_subscribe with the "logs" parameter pushes logs to your client as they are mined. No chunking needed. No 10000 result limit. The subscription persists until you close it or the connection drops.
WebSocket subscriptions work for new blocks only. They cannot replay historical logs. For historical data, you still need chunked eth_getLogs calls. The page on HTTP vs WebSocket RPC covers when each transport fits.
Practical limits by provider
Different providers enforce different limits. Some cap at 10000 results but allow wider block ranges. Some cap both. Check your provider's documentation. If you run your own Geth or Erigon node, you control the limits. The --rpc.gascap and --rpc.evmtimeout flags affect log queries indirectly. The page on running your own node explains those settings.
When chunking fails
Chunking fails if every single block in your range produces more than 10000 logs. That is rare. It happens only on extremely active contracts during peak network usage. In that case, you need an archive node with higher limits. Or you switch to an indexed data service like The Graph.
For almost every use case, chunking works. Start with a conservative chunk size. Monitor your error rate. Adjust until the 10000 result error disappears.
This approach is not elegant. It is reliable. That is what matters when you are indexing events in production.
Not financial advice. starname.me publishes market data and general information about digital assets. Crypto assets are volatile and you can lose everything you put in. Nothing here is a recommendation to buy, sell or hold, and we make no price predictions.
Prices are sourced from third parties and may be delayed or wrong. Verify anything you intend to act on against a primary source.