An open and honest benchmark for blockchain indexers. Every number below comes from code in this repository, so you can run it yourself and check, and the tables are refreshed automatically by scheduled CI runs.
If you want to know how the numbers are produced, or what a column means, that is all in METHODOLOGY.md.
The benchmark started in May 2025 as a fork of Sentio's research. That repository was later closed, so Envio picked it up and has kept it current since. We are not affiliated with Sentio, and although the project now lives under the Envio organisation — its data is what the Envio landing page and the Blockchain Indexers in 2026 article cite — the point of it is a fair comparison.
Contributions are welcome — we already have some from the SQD team. Open an issue or a pull request to add an indexer, add a scenario, report a result that looks wrong, or improve the methodology. Indexer teams especially: nobody knows your tool better than you do. Or just come and ask on Discord or Telegram.
How well does an indexer cope with data it has to read back? Every rETH transfer changes a balance, so for each one the indexer has to find the right row, update it, and save it again. The scenario follows the benchmark on the Ponder landing page.
| tool | source | events/s | blocks/s | vs best | data | storage |
|---|---|---|---|---|---|---|
| Envio Indexer | HyperSync | 8,182.4 | 57,701.3 | — | ✅ | Postgres 2.2 MB |
| Envio Subgraph | HyperSync | 7,649.6 | 53,720.8 | 1.1x slower | ✅ | Postgres 2.2 MB |
| Rindexer | HyperSync | 947.2 | 6,894.0 | 8.6x slower | ✅ | Postgres 5.2 MB |
| Squid SDK | SQD Network | 556.1 | 3,891.4 | 14.7x slower | ✅ | Postgres 2.2 MB |
| Envio Indexer | RPC | 341.9 | 2,555.6 | 23.9x slower | ✅ | Postgres 2.3 MB |
| Rindexer | RPC | 322.8 | 2,413.8 | 25.3x slower | ✅ | Postgres 4.9 MB |
| Envio Subgraph | RPC | 241.1 | 2,263.7 | 33.9x slower | ✅ | Postgres 2.2 MB |
| Ponder | RPC | 62.0 | 816.1 | 132x slower | ✅ | Postgres 3.3 MB |
| Subgraph | RPC | 31.4 | 412.8 | 261x slower | ✅ | Postgres 7.0 MB |
| Substreams | StreamingFast | 28.7 | 377.8 | 285.1x slower | ✅ | Postgres 2.2 MB |
| SubQuery | RPC | 25.3 | 332.5 | 323.9x slower | ❓ (1) | Postgres ~4.4 MB |
| Squid SDK | RPC | 17.3 | 199.7 | 474.1x slower | ❓ (2) | Postgres ~2.4 MB |
(1) SubQuery — missing 0.12% of the data: the verification range was not finished within 300s (2) Squid SDK — missing 32% of the data: the verification range was not finished within 300s
How this case works, and how to run it →
How fast can an indexer write? Every USDC transfer is stored once, with nothing to aggregate and nothing to look up first. This is the ingestion path on its own.
| tool | source | events/s | blocks/s | vs best | data | storage |
|---|---|---|---|---|---|---|
| Rindexer | HyperSync | 78,416.1 | 8,379.5 | — | ✅ | Postgres 3.4 MB |
| Envio Indexer | HyperSync | 62,889.7 | 6,872.5 | 1.2x slower | ✅ | Postgres 1.4 MB |
| Envio Subgraph | HyperSync | 35,038.6 | 3,803.2 | 2.2x slower | ✅ | Postgres 1.4 MB |
| Squid SDK | SQD Network | 11,641.2 | 1,399.2 | 6.7x slower | ✅ | Postgres 1.4 MB |
| Rindexer | RPC | 10,949.5 | 1,342.3 | 7.2x slower | ✅ | Postgres 3.4 MB |
| Substreams | StreamingFast | 2,653.1 | 310.7 | 29.6x slower | ✅ | Postgres 1.4 MB |
| Envio Indexer | RPC | 1,311.9 | 166.9 | 59.8x slower | ✅ | Postgres 1.4 MB |
| Squid SDK | RPC | 1,042.0 | 140.1 | 75.3x slower | ✅ | Postgres 1.4 MB |
| Envio Subgraph | RPC | 834.5 | 118.1 | 94x slower | ✅ | Postgres 1.4 MB |
| Subgraph | RPC | 108.2 | 13.3 | 724.6x slower | ✅ | Postgres 2.8 MB |
| Ponder | RPC | 37.4 | 4.6 | 2095.7x slower | ✅ | Postgres 2.5 MB |
| SubQuery | RPC | 31.8 | 3.9 | 2468.5x slower | ✅ | Postgres 1.9 MB |
How this case works, and how to run it →
Not everything an indexer needs is in the logs. Every approval on the eight busiest ERC-20s is followed by a read of the allowance at that block: 15,703 calls, 200ms each, answered by the benchmark so every tool waits the same. Nothing limits how many a tool may have outstanding, so the rows differ by how many of those waits it takes at once.
| tool | source | events/s | blocks/s | vs best | data | storage |
|---|---|---|---|---|---|---|
| Squid SDK | SQD Network | 13,164.4 | 869.9 | — | ✅ | Postgres 8.3 MB |
| Envio Indexer | HyperSync | 12,541.7 | 826.6 | — | ✅ | Postgres 8.4 MB |
| Rindexer | HyperSync | 7,704.4 | 494.0 | 1.7x slower | ✅ | Postgres 7.3 MB |
| Rindexer | RPC | 7,404.2 | 475.7 | 1.8x slower | ✅ | Postgres 7.3 MB |
| Envio Indexer | RPC | 2,685.3 | 191.6 | 4.9x slower | ✅ | Postgres 8.2 MB |
| Envio Subgraph | HyperSync | 2,050.8 | 144.0 | 6.4x slower | ✅ | Postgres 8.6 MB |
| Squid SDK | RPC | 824.8 | 43.7 | 16x slower | ✅ | Postgres 8.3 MB |
| Envio Subgraph | RPC | 398.1 | 23.2 | 33.1x slower | ✅ | Postgres 8.5 MB |
| Subgraph | RPC | 59.8 | 3.8 | 220.1x slower | ❓ (1) | Postgres ~20.5 MB |
| Ponder | RPC | 36.0 | 2.5 | 365.9x slower | ❓ (2) | Postgres ~11.7 MB |
| SubQuery | RPC | 4.6 | 0.3 | 2849.4x slower | ❓ (3) | Postgres ~16.2 MB |
| Substreams | StreamingFast | — | — | — | — (4) | — |
(1) Subgraph — missing 6.0% of the data: the verification range was not finished within 300s (2) Ponder — missing 43% of the data: the verification range was not finished within 300s (3) SubQuery — missing 93% of the data: the verification range was not finished within 300s (4) Substreams — its contract calls run against the Substreams server's own node, not a given endpoint
How this case works, and how to run it →
What happens when you do not know the contracts up front? The indexer watches the Safe proxy factories, and every one of the 82,268 proxies they create becomes another contract it has to follow from that moment on.
| tool | source | events/s | blocks/s | vs best | data | storage |
|---|---|---|---|---|---|---|
| Envio Subgraph | HyperSync | 10,781.9 | 3,806.2 | — | ✅ | Postgres 14.0 MB |
| Envio Indexer | HyperSync | 9,267.1 | 3,271.5 | 1.2x slower | ✅ | Postgres 14.0 MB |
| Rindexer | HyperSync | 7,148.9 | 2,523.7 | 1.5x slower | ✅ | Postgres 11.4 MB |
| Envio Subgraph | RPC | 3,834.1 | 1,353.5 | 2.8x slower | ✅ | Postgres 14.0 MB |
| Envio Indexer | RPC | 3,622.6 | 1,278.8 | 3x slower | ✅ | Postgres 14.0 MB |
| Rindexer | RPC | 3,605.1 | 1,272.7 | 3x slower | ✅ | Postgres 11.4 MB |
| Squid SDK | SQD Network | 2,531.6 | 905.7 | 4.3x slower | ❌ (1) | Postgres 13.8 MB |
| Squid SDK | RPC | 333.7 | 117.8 | 32.3x slower | ❌ (2) | Postgres 13.8 MB |
| Ponder | RPC | 282.7 | 96.5 | 38.1x slower | ❓ (3) | Postgres ~25.3 MB |
| Substreams | StreamingFast | 266.7 | 66.5 | 40.4x slower | ❓ (4) | Postgres ~14.5 MB |
| Subgraph | RPC | 28.7 | 10.6 | 375.7x slower | ❓ (5) | Postgres ~42.3 MB |
| SubQuery | RPC | 0.0 | 0.0 | — | ❓ (6) | — |
(1) Squid SDK — 921 of 927 safe setups missing; 10 of 11 fallback handler changes missing; 211 of 293 module enables missing (2) Squid SDK — 921 of 927 safe setups missing; 10 of 11 fallback handler changes missing; 211 of 293 module enables missing (3) Ponder — missing 0.18% of the data: the verification range was not finished within 300s (4) Substreams — missing 5.6% of the data: the verification range was not finished within 300s (5) Subgraph — missing 90% of the data: the verification range was not finished within 300s (6) SubQuery — indexed nothing in 300s, so there was no data to verify
How this case works, and how to run it →
Every USDC transfer on Solana, through the chain's busiest program. Solana makes that harder than it sounds: transfers hide inside swaps and routers, and many never say which token they moved. The scenario follows StreamingFast's SPL token Substreams.
| tool | source | events/s | blocks/s | vs best | data | storage |
|---|---|---|---|---|---|---|
| Envio Indexer | HyperSync | 16,204.7 | 246.4 | — | ✅ | Postgres 37.8 MB |
| Substreams | StreamingFast | 3,856.5 | 126.9 | 4.2x slower | ✅ | Postgres 62.7 MB |
| Squid SDK | SQD Network | 996.8 | 33.5 | 16.3x slower | ✅ | Postgres 37.7 MB |
| Carbon | RPC | 563.2 | 18.9 | 28.8x slower | ✅ | Postgres 40.4 MB |
How this case works, and how to run it →
Six scenarios from the original 2025 research, kept here for reference. They are total sync times rather than throughput rates, and they predate the current methodology, so do not compare them with the tables above.
| Case | Sentio | Envio HyperSync | Envio HyperIndex | Ponder | Subsquid | Subgraph | Sentio_Subgraph | Goldsky_Subgraph |
|---|---|---|---|---|---|---|---|---|
| case_1_lbtc_event_only | 8m | 3m | 1h40m | 10m | 3h9m | 2h36m | ||
| case_2_lbtc_full | 6m | 1m | 45m | 34m | 1h3m | 56m | ||
| case_3_ethereum_block | 18m | 7.9s | 33m | 1m‡ | 10m | 15m | ||
| case_4_on_transaction | 17m | 1m26s | 33m | 7m | N/A | |||
| case_5_on_trace | 16m | 41s | N/A§ | 2m | 8m | 1h21m | ||
| case_6_template | 19m | 8s | 21m | 2m | 19m | 10m | 20h24m |
Want to try it yourself? Each scenario page above has its own setup instructions, or you can run the whole suite the way CI does:
ENVIO_API_TOKEN=your-token SQD_API_KEY=your-key node scripts/run-benchmarks.tsArguments are passed straight through, so node scripts/run-benchmarks.ts envio ponder --duration=100 picks which indexers to run and how long the window is, and --cases=erc20-transfer-events narrows it to one scenario. You will need an Envio API token for the RPC endpoint and the ground truth; the SQD key is only needed for the Squid SDK run that reads from SQD Network.