SolidRPC plans change on 28th October. Some prices are going up. Lock in today’s pricing and limits by activating before then. See what’s changing.
Skip to main content
Guides
Category
Performance
Reading time
9 minutes
Published

How to benchmark an EVM RPC endpoint before production

A fast eth_blockNumber response does not tell you whether an RPC endpoint can complete your indexer backfill, serve an old eth_call, or keep a wallet responsive during a traffic burst. Those are different jobs with different failure modes.

A useful benchmark starts with the work your application must finish, then tests the exact methods and block depths involved. It checks response correctness and useful throughput alongside latency, timeouts, and cost. This guide shows how to build that test, run it fairly, and decide whether an endpoint is ready for production.

Section 01

What decision should the benchmark answer?

Write an acceptance statement before sending load. For a wallet, it might be that balance and receipt reads finish within the application's deadline while transaction status remains coherent. For an indexer, it might be that live blocks stay current while a historical backlog shrinks and every expected event is committed once. For a trace tool, it might be that a specified transaction and tracer return the required schema at the oldest supported depth.

List the chain, method, parameters, oldest block, expected result, request deadline, steady rate, burst shape, and recovery objective for each job. Decide which failures are disqualifying. An endpoint that cannot answer a required historical method should fail qualification even if its average latency is excellent. Use the Ethereum JSON-RPC method reference to define standard request shapes, then verify the behavior on the target EVM chain.

Section 02

Which requests belong in a representative test corpus?

Start from a sample of your application's real request mix with credentials and personal data removed. Preserve method, parameters that determine work, response size, and time of day. Group requests by outcome: current-state reads, pinned historical state, blocks and receipts, sparse and dense logs, traces or simulations, and transaction submission if your product uses it. Do not turn a hundred varied calls into a hundred identical head pings.

Choose fixed fixtures with independently checked results. Save the chain ID, block number and hash, contract address, transaction hash, expected log count or state value, and expected response shape. For moving-head tests, record the block number, hash, and timestamp returned with each sample. Ethereum's `eth_getLogs` specification distinguishes range and block-hash filters, so include the form your application actually sends.

Keep cache-sensitive requests honest. A repeated request for one old block measures a different path from a changing set of blocks. Run both only if both occur in production, and label them separately. Use the same fixtures and request schedule when comparing candidates.

Section 03

How do you check correctness before measuring speed?

Parse each JSON-RPC response, match its id to the request, and distinguish result from error. The JSON-RPC 2.0 specification defines these as mutually exclusive outcomes. An HTTP success alone is not proof that the method succeeded. Conversely, an empty log array or a null lookup can be a valid result for the selected fixture.

Compare fixed-block responses with your checked fixtures. For a sequence of blocks, verify hashes and parent continuity. For logs, check block hash, transaction hash, log index, and expected event count. For traces, validate the exact method, tracer options, and fields your consumer uses. Mark unsupported methods, missing history, wrong values, malformed results, timeouts, and throttling separately. Never count a wrong answer as a fast success.

When several reads form one decision, pin them to one block identity where supported. The Ethereum block parameter includes latest, safe, finalized, and explicit numbers, but a moving tag can resolve to a different block between calls. Test the exact reference form your application will use and record how the target chain handles it.

Section 04

What load profile exposes production behavior?

Run a short low-rate check first to validate fixtures and response parsing. Then run separate scenarios for ordinary steady traffic, a realistic burst, and recovery work such as a backfill alongside live requests. Keep the offered request rate independent of response time. Otherwise a slow endpoint makes a closed-loop test send fewer requests, concealing the overload you intended to measure. API load-testing guidance explains how a constant arrival rate models a requested rate and how thresholds turn a test into a pass or fail decision.

Set a client deadline and bounded retry policy before the run. Record both original operations and all attempts. A retry can improve the final completion rate while increasing traffic and tail latency. Keep live and recovery traffic separately labeled so a backfill cannot appear successful by starving user-facing reads. Run long enough to see sustained behavior, but stay within the agreed test allowance and stop if the test harms production traffic.

Warm and cold behavior may differ. Label the start of the run, discard warm-up only when the production workload truly has an equivalent warm state, and repeat on more than one time window. A single short run is a sample, not a durable performance claim.

Section 05

Which numbers belong on the scorecard?

Report results per chain and request class, then an application-wide total. Keep at least these measures:

  • Offered, started, and completed operations per second. If the load generator cannot start scheduled work, its missing iterations are test failures, not endpoint successes.
  • Valid results and correct application outputs within the deadline.
  • p50, p95, and p99 end-to-end latency for completed operations, plus the timeout rate. Do not calculate latency only from successful first attempts.
  • HTTP failures, JSON-RPC errors, malformed results, throttling, and retries as separate counts.
  • Response bytes and total bytes transferred, especially for logs and traces.
  • Head distance for live reads and backlog change for an indexer.
  • Cost per completed application operation using the candidate's current billing terms, if cost is part of the decision.

Load-test metric documentation defines dropped iterations, request duration, and failure counters. Use custom checks for semantic RPC correctness, because a transport metric cannot tell whether a returned block or log is the one your application expected. Compare percentiles only for like-for-like workloads and show sample counts.

Section 06

How do you test recovery and avoid a misleading comparison?

Repeat critical fixtures during a controlled disruption or planned service transition, using the same customer-facing route. Check that old state, logs, traces, and pinned-block reads still meet their acceptance rules. A head-only probe cannot establish historical continuity. If a transaction submission times out, treat its status as unknown and reconcile by its hash rather than generating a second signed transaction for the benchmark.

Use the same client location, network path, request corpus, concurrency policy, timeout, retry budget, and measurement window for each candidate. Run each at the capacity and plan you would actually buy. Record the date, plan, published limits, and test allowance, since terms and implementations change. Do not claim a universal winner from results for one chain, one region, or one method.

A fair comparison also separates endpoint behavior from the load generator. Monitor its CPU, memory, network, and missed schedule. If the generator saturates, fix the test harness and rerun before interpreting endpoint latency.

Section 07

When is the endpoint ready to ship?

Apply hard capability and correctness gates first. Every required chain, method, historical depth, response shape, and block-consistency check must pass. Then compare sustained valid completions, tail latency, recovery, and workload-adjusted cost against the thresholds written before the run. Do not average an unsupported trace or missing old state into a passing score.

Keep a reproducible record: sanitized request corpus, fixture hashes and expected answers, load schedule, deadline and retry settings, raw outcomes, summary by request class, and the date of the run. Re-run the critical fixtures after changing endpoint configuration, traffic mix, target network, or application requirements. The benchmark is evidence for a particular workload and time, not a permanent property of an RPC URL.

For focused follow-ups, use the `eth_getLogs` performance guide for range-heavy scans and the RPC failover guide for block consistency during recovery.

Section 08

EVM RPC benchmark checklist

  • Define the application operation and its pass criteria before testing.
  • Freeze real methods, parameters, chains, block depths, and verified fixtures.
  • Include live traffic, bursts, and recovery traffic in separate scenarios.
  • Check JSON-RPC results and block identity, not just HTTP status.
  • Record offered and completed work, p50/p95/p99, errors, timeouts, retries, bytes, and useful output.
  • Keep request pacing, deadlines, and retry budgets identical across candidates.
  • Test the exact purchased route during normal operation and a controlled disruption.
  • Save raw outcomes and reject a candidate that fails a required capability or correctness gate.

If these checks are repeatable, a provider decision can be revisited when the workload changes instead of resting on a screenshot of a single ping test.

Workload review

Want us to benchmark your current RPC stack?

Send your providers, routing setup, method mix, or monthly bills. We will compare the complete stack with one SolidRPC integration across cost, useful completion rate, failover, and operational work.