RPCBench
Which RPC endpoint is fastest — for this call, from this machine?
Vendor-neutral RPC benchmark CLI: compare JSON-RPC / REST providers (EVM, Solana, Substrate, Cosmos, Sui, NEAR, Starknet, Bitcoin, TON, Aptos) on latency, P50/P95/P99, error rate, freshness, and a ranked table — from this machine, with honest caveats. Read-only by default. No accounts. No telemetry. Localhost and RFC1918 are allowed (bench your own node). Use --family solana, --family substrate, --family cosmos, --family aptos, --family sui, --family near, --family starknet, --family bitcoin, or --family ton (or family: / auto in the endpoints file).
It is not a security scanner (Nodeprobe) and not validator monitoring (ValidatorPulse). Split: docs/BOUNDARY.md. How the numbers are computed: docs/METHODOLOGY.md. CI / Docker / Action: docs/CI.md. Prometheus textfile: docs/PROMETHEUS.md. Grafana: docs/GRAFANA.md. Site: ehsanhajian.github.io/RPCBench. Issues: github.com/ehsanhajian/RPCBench/issues.
Install
pip install rpcbench
Python 3.10+. rpcbench --version prints 0.6.1.
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Docker
docker pull ghcr.io/ehsanhajian/rpcbench:latest
docker run --rm -v "$PWD:/work" -w /work ghcr.io/ehsanhajian/rpcbench:latest \
compare --endpoints endpoints.yaml --out-dir out --ci --max-p95 500
Image tags publish on v* releases (ghcr.io/ehsanhajian/rpcbench). Local build: docker build -t rpcbench .
GitHub Action
name: rpcbench
on:
schedule:
- cron: "0 */6 * * *"
pull_request:
jobs:
compare:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ehsanhajian/RPCBench@v0.6.1
with:
endpoints: endpoints.yaml
budget: short
max-p95: "500"
max-error-rate: "0.05"
output-dir: rpcbench-out
# HTML/JSON/MD are uploaded as artifact rpcbench-report
Pin the Action with @v0.6.1 (or a later release tag).
The Action runs compare --ci with your SLO inputs, writes report.html / report.json / report.md under output-dir, uploads them as rpcbench-report, and fails the job when a budget is missed. Open report.html from the artifact locally. Details: docs/CI.md.
PRs run pytest, then a live smoke per family against public RPCs (eth_blockNumber, getSlot, chain_getHeader, status, ledger GET, sui_getLatestCheckpointSequenceNumber, NEAR status, starknet_blockNumber, getblockchaininfo, getMasterchainInfo; --samples 1 --warmup 0). No local node in CI; each family smoke passes if either public endpoint in that file is ok.
Start
rpcbench compare --endpoints endpoints.yaml
rpcbench compare --endpoints endpoints.yaml --workload wallet
rpcbench compare --endpoints endpoints.yaml --html -o report.html
The first command is the general mix at short size (verdict and route). --workload wallet or trading also runs simulate. --workload indexer also runs logs-range, archive, and lookback. --budget only changes how long to sample. A single call is --method eth_blockNumber. Lab flags (--batch, --websocket, --yellowstone, --rank-by, …) are in rpcbench compare --help-all.
Or a YAML/JSON file of named endpoints (keep API keys in a local file; do not commit it):
endpoints:
- name: eth-publicnode
url: https://ethereum.publicnode.com
family: evm
- name: sol-publicnode
url: https://solana-rpc.publicnode.com
family: solana
- name: dot-publicnode
url: https://polkadot-rpc.publicnode.com
family: substrate
- name: atom-publicnode
url: https://cosmos-rpc.publicnode.com
family: cosmos
- name: aptos-labs
url: https://fullnode.mainnet.aptoslabs.com/v1
family: aptos
- name: sui-publicnode
url: https://sui-rpc.publicnode.com
family: sui
- name: near-mainnet
url: https://rpc.mainnet.near.org
family: near
- name: starknet-publicnode
url: https://starknet-rpc.publicnode.com
family: starknet
- name: bitcoin-publicnode
url: https://bitcoin-rpc.publicnode.com
family: bitcoin
# user: rpcuser
# password: rpcpassword
# cookie: ~/.bitcoin/.cookie
- name: toncenter
url: https://toncenter.com/api/v2/jsonRPC
family: ton
# headers:
# X-API-Key: YOUR_KEY
Compare one family at a time (--family ton or only that family’s rows in the file). run is the same command as compare. rpcbench diff old.json new.json compares two JSON runs. rpcbench merge eu.json us.json joins multi-vantage reports (same seed/workload).
Report
Default is Fastest, a production-readiness Verdict, a Route (primary / fallback), and the ranked list. --verbose is the full dump (including Signals). --json / -o is always the complete payload. --html -o report.html is a standalone file (inline CSS/SVG, no CDN): ranking with sample sparklines, a provider × method Heatmap, Signals (problem / why / next), and print CSS. --md is a pasteable GitHub markdown table with aligned columns (ranking, P95, errors, rel, freshness, verdict). --csv is one flat row per provider (run, rank, latency, verdict, transport). -o report.csv writes that CSV and keeps the CLI table. --prometheus dumps Prometheus textfile metrics (latency, error rate, rps, reliability, histogram); -o metrics.prom or --out-dir writes metrics.prom. Details: docs/PROMETHEUS.md. Grafana pack: docs/GRAFANA.md (grafana/dashboards/rpcbench.json). rpcbench diff old.json new.json compares two JSON runs (P95 delta, winner change, new signals) and exits 1 in CI if the previous primary got worse beyond the similar-band. --history DIR appends a JSON snapshot after a run so diff can use --history DIR. rpcbench merge a.json b.json joins multi-vantage JSON (same seed/workload) into a per-region P95 table and global rollup. Every report prints a Cite line (version, git sha, family, vantage, UTC) so the numbers can be reproduced.
Default
--workload / --profile mix (Coverage table: which required methods succeeded)
rpcbench compare --endpoints endpoints.yaml --workload --budget short
rpcbench compare --endpoints endpoints.yaml --workload wallet --budget short
HTTP timing (--new-connection --verbose)
--verbose
HTML report (--html -o report.html) — colored ranking, heatmap, and signals table; works offline; print-ready
--profile mix heatmap (provider × method; skip/miss is product fit, not a scan)
The default CLI prints, in order:
- Summary — Fastest (P95 by default; similar-band co-winners, not 81ms vs 84ms)
- Verdict — production-ready / risky / not ready for this workload, this run. Labels:
fast+stable,slow+reliable,similar,stale,stale-risk,timeout,rate-limited,coverage,disagree,jitter,failed,errors. Not an SLA. - Route — primary and fallback among ready endpoints. Stale or disagreeing nodes are never primary. Fallback prefers a different error class (not two 429s). Named when 2+ providers are ready; otherwise fallback is
none. One paragraph explains the choice. - Ranking — one table, ordered by
--rank-by; similar share a place; high error, stale, or disagree is~; failed last.relis the 0–100 reliability score for this run. - Notes — one line per endpoint that hit
rate_limiton timed samples or tags (merkle rate_limit=2 tags=2) - Batch — when
--batchis set: support, batch vs serial wall-clock, ratio.skipmeans the extra read did not run (budget/duration). Not mixed into ranking. - Concurrency — when
--concurrencyis set: overlapping extra POSTs vs the same N serial (per-request P50/P95, ratio, error count). Default is off. Not mixed into ranking. - Throughput — when
--throughputis set: extra serial POSTs, successful req/s, duration, and completed count.--rpscaps starts. 429 is a rejected request, not a crash. Default is off. Not mixed into ranking. 8b. Shape — when--shape flat|ramp|spike|soakis set: bounded load curve with a time series of RPS / P95 / error rate (CLI + HTML + Prometheus). Short defaults. Not mixed into ranking. - Logs range — when
--logs-rangeis set: pinnedeth_getLogsat 1 / 10 / 100 / up to N blocks (latency, bytes, n, truncation). Mix logs stay one block. A provider that only dies at 1000 blocks shows here. Not mixed into ranking. - Archive — when
--archiveis set:eth_getBalanceat genesis, classified yes / no / unknown / rate-limited. Missing archive is a capability result, not a crash. Not mixed into ranking. - History — when
--lookback Nis set: timedeth_getBalanceat pin−N vs latest (latency, error rate, ratio). Endpoints without archive/history skip with a reason. Not mixed into ranking. - WebSocket — when
--websocketis set: connect time,eth_subscribenewHeadslatency, first-event delivery, missed heads, and disconnects over a bounded window. Missingws/websocketURL is not configured. Not mixed into ranking. - Yellowstone — when
--yellowstoneis set (Solana): gRPC first-seen race across endpoints with agrpc/yellowstoneURL, wins, lag histogram. Geography dominates; not tx landing. Missing gRPC is not configured. Not mixed into ranking.
--verbose adds the rest (same numbers, no data loss):
- Comparison — YAML order (failed rows stay in place; head / lag / fresh / hash / match; rel)
- Reliability — breakdown of
rel(errors, timeouts, tail, mix coverage). Not an SLA. Not a security score. - Signals — each problem / why / next (routing and config: raise
--timeout, pick another endpoint, pin--block). Not CVE language, not hardening. - Coverage — active mix only: each required method is
ok, an error class, orskipif not offered (skip/unsupported/skip/restricted/skip/timeoutfor optional trace/simulate). A miss is product fit (indexereth_getLogs404s), not a vuln. Compact--workload/--profile mixprints this table; JSON iscoverage. - Methods — per-method P50/P95/P99 and errors when a mix is active (ranking still uses the whole mix)
- Timing — handshake (DNS+TCP+TLS) vs server wait vs payload (body+parse). Not mixed into ranking. Default is keep-alive;
--new-connectionis a cold handshake every request - Transport — negotiated HTTP proto (
1.1/2), content-encoding, request/response bytes. Size vs latency is in HTML (log bytes, colored by method). Not mixed into ranking.--http2asks for HTTP/2;--http1forces 1.1 - Tags — one paired
latest/safe/finalizedsnapshot (skipped with a reason if the tag is missing) - Burst — burst vs steady error rate and recovered rps when
--burstis set (same request budget). Extra tag 429s show astags=N, not in timedn/err. - Providers — one table: redacted URL, client, n/err, p95, head/lag/fresh/match, histogram (
≥1s=3), note. Per-sample rows follow. - Capabilities — who answered this method (and whether batch was supported, when enabled)
On a TTY, ok is green and fail is red (NO_COLOR or a pipe turns color off). While samples collect, a live table (P50/P95, err, rps, sparkline) updates unless --plain or --ci. --web (or rpcbench ui) opens a live browser UI (default 127.0.0.1) with latency / errors / RPS / lag charts; on a VPS use --web-host 0.0.0.0 to reach it from another machine (no auth). Stop aborts and still writes reports, then shows the HTML report. Ctrl-C stops early and still writes JSON/HTML. Reports never print API keys, bearer tokens, or header values.
How a run works
Numbers and caveats: docs/METHODOLOGY.md.
Workload
- Paired by default: one shared read-only sequence; each sample is raced to every provider at the same time.
--sequentialis A-then-B. --budgetpicks a named size (short/standard/long). That sets how many mix rounds to take.--max-requestsis the HTTP cap (how many requests the run may send).--samplesand--warmupoverride the named size.longis more samples only — not archive, WebSocket, or tracing unless the workload asks.--workload general|wallet|indexer|trading|nft|tracingruns a documented, weighted, read-only mix. Omit the name for general.--profile mixis the same as--workload general.--samplesis per mix round; a step’s weight is how often it appears in that round. Ranking uses the whole mix, not one cheap head read. Coverage is those methods only:ok, error class, orskipif not offered. A failingeth_getLogsis a miss for--workload indexer, not a vuln;--workload walletdoes not send logs and emphasizeseth_getBalance/eth_call/eth_estimateGas.--workload tracingtimes optionaltrace_blockanddebug_traceCall; missing traces skip, not a crash. Payloads and weights: docs/METHODOLOGY.md.--simulateadds read-onlyeth_call,eth_estimateGas, andeth_simulateV1(fixture tx, never a send). wallet and trading turn this on.--no-simulateturns it off. Missing simulateV1 is skip, not a crash, and does not tank ranking. Details: Read-only simulation.--profile FILE.yamlis a custom mix you write (methods, weights, optional timeout/notes).source: latest_head|recent_block|known_contract|seeded_addressfills params from a shared chain snapshot and--seedso every provider gets the same sequence. If the chain cannot supply data, documented fixtures (latest, zero address) are used. Full YAML example, sources, seed, and fallback: Custom YAML profiles.- Burst is opt-in (
--burst N, max 8). The first N timed samples overlap; the rest are a steady phase, optionally capped with--rps. Burst splits the existing sample budget and does not add requests. Burst vs steady error rate and rps are reported separately. Tag 429s aretags=Non that table (not mixed into timed n/err). Default is off (--burst 0,--rps 0). Ramp/spike/soak shapes are a later issue. - Concurrency is opt-in (
--concurrency N, omit N for 4, max 8). After timed samples, RPCBench sends N overlapping HTTP POSTs of the primary method, then the same N calls one-by-one, and reports per-request concurrent P50/P95 vs serial P50 plus the concurrent error count. Adds 2N requests per endpoint. Default is off (--concurrency 0). Not an unbounded load test. - Throughput is opt-in (
--throughput N, omit N for 20, max 64). After timed samples, RPCBench sends N extra serial POSTs of the primary method and reports successful req/s, wall duration, and completed count.--rpscaps how often those starts fire. HTTP 429 is a rejected request (rate_limit), not a crash. Adds N requests per endpoint. Default is off (--throughput 0). Not concurrent fan-out and not an unbounded load test. - Batch is opt-in (
--batch N, omit N for 3, max 8). After timed samples, RPCBench sends one JSON-RPC array of N copies of the primary method, then the same N calls one-by-one, and reports wall-clock and the serial/batch ratio. A single-object error means the provider does not support batch (capability, not a crash). Partial item errors are markedpartial. Adds 1+N requests per endpoint. Default is off (--batch 0). Not HTTP/2 multiplexing. - Logs range is on for indexer (1000 blocks) and otherwise opt-in (
--logs-range N, omit N for 1000,0off). After the pin, the same zero-addresseth_getLogsis sent at 1, 10, 100, and up to N blocks. Mix logs staylatest→latest. Too-short chains skip withhead. Truncation and 1000-only failures show in the Logs range table. Adds up to 4 requests per endpoint. --archiveprobes historical state with oneeth_getBalanceof the zero address at genesis. indexer turns this on.--no-archiveturns it off. Classified yes / no / unknown / rate-limited. Chains shorter than 128 blocks skip withhead. Missing archive is a capability result, not a crash, and does not change ranking. Details: Archive / historical state.--lookback Ntimeseth_getBalanceatpin−Nvslatest(indexer uses 1000; otherwise omit N for 1000,0off). Reuses--archiveto skip when the node has no history. Not mixed into ranking. Details: Historical queries.--websocket SECtimes WebSocket connect,eth_subscribenewHeads, and first-event delivery over a bounded window (omit SEC for 3s, max 10s). Needs an optionalws/websocketURL on the endpoint (ws://orwss://). Missing WS is not configured. Disconnects and missed block-number gaps are counted in that window. Does not consume--max-requests. Not mixed into ranking. Details: WebSocket subscribe.--yellowstone SECraces Solana Yellowstone/gRPC slot streams across endpoints (omit SEC for 3s, max 10s). Needs an optionalgrpc/yellowstoneURL per endpoint. Missing gRPC is not configured. Reports first-seen wins and lag. Geography dominates; not tx landing. Does not replace HTTP Solana. Optional deps:pip install 'rpcbench[yellowstone]'. Not mixed into ranking. Details: Yellowstone / gRPC first-seen.
Stats
-
Warmup is excluded from min/mean/max, jitter, percentiles, error rate, and the histogram.
-
P50/P95/P99 are nearest-rank over successful samples. Jitter is the sample standard deviation of those samples (needs n≥2). P99 is the slowest sample until n≥100 (flagged below that).
-
Histogram is where successes landed: empty buckets are omitted (
≥1s=3, or<50ms=8 ≥1s=8when split). Buckets:<50ms,<100ms,<250ms,<1s,≥1s(same edges in JSON). -
HTTP timing splits each successful sample into handshake (DNS+TCP+TLS), server wait after the connection is ready, and payload (body download + JSON parse). Ranking still uses total RTT. Default reuses keep-alive connections (handshake is ~0 after warmup).
--new-connectionopens a fresh TCP/TLS session every request so distance vs node time is visible. TLS here is handshake latency, not a certificate check. -
HTTP transport records the negotiated protocol (
1.1or2),Content-Encoding(gzip,br, …), request bytes, and response wire bytes. Hugeeth_getLogspayloads and missing compression look like slow nodes. HTML Size vs latency is log(bytes) vs RTT, colored by method, so a 40-byte head and a 7kBgetBlockare not stacked on the Y-axis. Ranking still uses total RTT. Default is HTTP/1.1.--http2asks for HTTP/2 via ALPN (falls back to 1.1).--http1forces HTTP/1.1. Not a TLS or CORS check. -
Error rate is failed/attempted, with a class (timeout, connection, HTTP 4xx/5xx, rate_limit, JSON-RPC, malformed).
rate_limitis HTTP 429 or a CU/throttle JSON-RPC message — reliability, not a scan. -
rps in the ranking table is
1000 / mean_msfor this probe — not parallel throughput.--throughput Nis a bounded extra-read of successful req/s over a serial window (duration and completed count in that table).--rps Ncaps starts after--burstand during--throughput, not that formula. -
Reliability
relis 0–100 for this run (not an SLA, not a security score). Same samples always produce the same score:rel = round( 50×(1−error_rate) + 20×(1−timeout_share) + 20×(1−tail) + 10×coverage )timeout_shareis timeouts / attempted.tailis 0 when P99=P50 and 1 when P99/P50 ≥ 3.coverageis the fraction of mix steps with at least one success (1.0 for a single method that answered). A 100% error run is 0. A clean run with a flat tail is 100.--verboseprints the four parts. JSON isreliability(score plus breakdown).
Ranking
- Default is P95 of successes (over the mix when
--workload/--profile mix). Override with--rank-by p50|p95|p99|mean|rps(throughput=rps). Lower latency wins; higher rps wins. - Similar-band (default 10%) shares a place when the worse value is within that fraction of the better. Error rate above the same band, a stale head, a disagreeing block hash, or a coverage miss (a required mix step never succeeded) is not a numbered place (
~). Failed (n_ok=0) never take Fastest. - Route names a primary and fallback among ready endpoints (never stale or disagree). Primary is the best reliability score within the similar-band of the fastest ready node, preferring known freshness and a matching hash. Fallback is the next ready endpoint; if primary had a timed error class, fallback skips others with that same class when a diverse ready alternative exists. One paragraph in the compact CLI explains the choice. JSON is
route.
Extra reads
These are not mixed into latency stats or Fastest.
- Freshness is lag vs the cohort’s upper-median
eth_blockNumberin the same window. Default--stale-blocks 2. Tables print yes when lag is within that tolerance, stale when it exceeds it (Ranking note may add~Ns). Lag time uses--block-timeor a known chain frometh_chainIdalready in the mix (12s on Ethereum). Extra head reads happen only when the workload has noeth_blockNumber. JSON still usesfresh/stale. - Consistency is whether providers return the same block hash at one pinned height (default: that cohort median).
--block HEX|Npins the check when heads naturally diverge by one block. A unique majority hash is canonical; a split is disagreement for everyone who returned a hash. Tables print yes / no under match. Missing/unparseable hashes are unknown, not disagree. JSON still usesagree/disagree. Not fork choice and not a security finding. - Client is a volunteered
web3_clientVersionstring stored as a label (Erigon vs Geth). Missing or hex-only results are omitted. Not a disclosure finding, not outdated-client recon, not a CVE check. - Tags are one paired
eth_getBlockByNumbersnapshot each forlatest,safe, andfinalized. Latency and freshness are per tag vs that tag’s cohort. Unsupported tags are skipped with a reason and do not change Fastest. Full P95 of one tag is--method eth_getBlockByNumber --params '["finalized", false]'. - Batch is one JSON-RPC array of N calls vs the same N sent serially (
--batch N). Wall-clock and ratio are extra reads. Unsupported batch isbatch_unsupported; partial item errors arepartial. Not mixed into Fastest. - Concurrency is N overlapping HTTP POSTs of the primary method vs the same N serial (
--concurrency N). Per-request P50/P95, ratio vs serial, and concurrent error count. Bounded extra read (max 8), not a load generator. Not mixed into Fastest. - Throughput is N extra serial POSTs of the primary method (
--throughput N). Successful req/s, duration, and completed count.--rpscaps starts. 429 is rejected, not a crash. Bounded extra read (max 64), not concurrent fan-out. Not mixed into Fastest. - Logs range is opt-in (
--logs-range, omit N for 1000). After the pin is known, RPCBench sends the sameeth_getLogs(zero address, no topics) at 1, 10, 100, and up to N blocks ending at that pin. Mix catalogs still uselatest→latest. A range is skipped with reasonheadwhen the chain is shorter than N blocks. Truncation (result cap / “too many logs”) is a table cell, not a ranking change. Adds up to 4 requests per endpoint. - Archive is opt-in (
--archive). After the pin is known, oneeth_getBalanceof the zero address at genesis classifies the endpoint yes / no / unknown / rate-limited. Too-short chains skip withhead. Not mixed into ranking. Adds 1 request per endpoint. - History is opt-in (
--lookback N, omit N for 1000). Timedeth_getBalanceat pin−N vs latest.--lookbackturns on archive detection; a pruned node skips witharchive. Not mixed into ranking. Adds up to 2 requests per endpoint. - WebSocket is opt-in (
--websocket SEC, omit SEC for 3s, max 10s). Connect +eth_subscribenewHeadson the optionalws/websocketURL. Missing WS is not configured. Reports connect, subscribe, first-event, missed heads, and disconnects. Does not consume--max-requests. Not mixed into ranking. - Yellowstone is opt-in (
--yellowstone SEC, omit SEC for 3s, max 10s). Solana gRPC first-seen race on optionalgrpc/yellowstoneURLs. Missing gRPC is not configured. Geography dominates; not tx landing. Does not replace HTTP Solana. Not mixed into ranking.
Capture and replay
rpcbench record writes a JSONL capture (one method + params object per line) from the same mix run would send. rpcbench replay --from FILE sends that sequence in lockstep and compares canonicalized bodies among who answered. A down or 429 node is status/error on that row, not a body mismatch. --verbose prints a unified diff when bodies disagree. Write methods (eth_send*, personal_*, …) are blocked unless --allow-writes. Not mixed into ranking. Details: Traffic capture and replay.
JSON
--json or -o FILE includes mode, seed, sequence_id, connection (keepalive or new), http (1.1 or 2), a watermark (version, git sha, UTC, budget, workload, seed, family, vantage, sample counts, plus methodology and boundary URLs), coverage (active mix steps only), reliability (0–100 this-run score plus breakdown; not success rate alone), verdict (ready / risky / not_ready plus kind and problem/why/next signals), route (primary / fallback / why), per-provider id (URL fingerprint, not printed in the CLI table), per-sample pairs (body hashes), jitter_ms, histogram, freshness, consistency, client, tags, burst, batch (size, supported, wall-clock vs serial), inflight (overlapping extra POSTs vs serial: percentiles, ratio, errors), throughput (serial extra-read: successful req/s, duration, completed count), logs_range (pinned getLogs windows: latency, bytes, n, truncation), archive (genesis-state yes / no / unknown / rate-limited), history (pin−N vs latest latency, error rate, ratio), websocket (connect / subscribe / first-event ms, n, missed heads, disconnects), yellowstone / yellowstone_race (gRPC first-seen wins, lag, histogram), HTTP timing percentiles, transport (proto, encoding, bytes), and burst phases.
--md is GitHub-flavored markdown with the same ranking numbers as JSON (P95, err, rel, fresh, match, verdict), plus Transport, Batch, Concurrency, Throughput, Logs range, Simulation, Trace, Debug, Archive, History, WebSocket, and Yellowstone tables when that run measured them. --csv is one row per provider with the same ranking, verdict, transport, batch, inflight, throughput, logs-range, simulate, trace, debug, archive, history, websocket, and yellowstone fields. --prometheus dumps latency / error / rps / reliability / histogram as Prometheus textfile metrics (docs/PROMETHEUS.md). rpcbench diff reads two of these JSON files. Not a security finding.
Flags
Happy path is compare --endpoints FILE (general, short). Named jobs turn extras on: wallet and trading add simulate; indexer adds logs-range, archive, and lookback. --budget long does not. Overrides (--no-simulate, --no-archive, --logs-range 0, --lookback 0, --websocket 0) and the rest of the lab flags are rpcbench compare --help-all. --profile FILE.yaml is a custom mix. --preset, --profile mix, and run still work.
--budget is a named size (how long to sample). --max-requests is the HTTP cap. --samples / --warmup override the named size.
--budget |
Samples | Warmup | Timeout | Stop after |
|---|---|---|---|---|
short |
3 | 0 | 5s | 30s |
standard |
10 | 1 | 10s | 600s |
long |
50 | 2 | 15s | 1800s |
| Flag | Default | |
|---|---|---|
--budget |
short with no other flags; else standard |
Named size in the table above. Does not enable archive, WebSocket, or tracing |
--samples |
10 | Timed mix rounds after warmup (overrides --budget). Each round sends sum(weights) calls |
--warmup |
1 | Requests excluded from stats (overrides --budget) |
--timeout |
10s | Per-request timeout (overrides --budget) |
--max-requests |
128 | HTTP cap for the whole run (hard cap RPCBENCH_MAX_REQUESTS, default 10000) |
--max-duration |
600s | Stop and still print a report (overrides --budget; 0 = no limit) |
--concurrency |
0 | Overlap N extra POSTs vs N serial (0=off, omit N for 4, max 8). Extra 2N requests/endpoint. Not mixed into ranking |
--burst |
0 | Overlap the first N timed samples (0=off, max 8). Same request budget |
--rps |
0 | Cap starts/sec after --burst and during --throughput (0=off). Does not raise the budget |
--throughput |
0 | Extra serial POSTs for successful req/s (0=off, omit N for 20, max 64). Extra N requests/endpoint. 429 is rejected |
--shape |
off | Bounded load shape flat / ramp / spike / soak (time series RPS/P95/err). Short defaults; not mixed into ranking |
--batch |
0 | JSON-RPC batch of N vs N serial (0=off, omit N for 3, max 8). Extra 1+N requests/endpoint |
--logs-range |
0 (1000 on indexer) | Pinned eth_getLogs at 1/10/100/up to N blocks (0=off, omit N for 1000). Mix logs stay 1 block |
--simulate |
on for wallet and trading | Add read-only eth_call / eth_estimateGas / eth_simulateV1. --no-simulate turns it off. Missing simulateV1 is skip |
--archive |
on for indexer | Probe eth_getBalance at genesis. yes / no / unknown / rate-limited. --no-archive turns it off. Not mixed into ranking |
--lookback |
0 (1000 on indexer) | Timed eth_getBalance at pin−N vs latest (0=off, omit N for 1000). Skips without archive |
--websocket |
0 | Connect + eth_subscribe newHeads (0=off, omit SEC for 3s, max 10s). Missing WS is not configured |
--yellowstone |
0 | Solana gRPC first-seen race (0=off, omit SEC for 3s, max 10s). Missing gRPC is not configured. Needs rpcbench[yellowstone] |
--ci / --strict |
off | Exit 1 when an SLO budget misses (needs --max-p95 / --max-error-rate / --max-lag). Reports still write. Disables live TUI |
--plain |
off | Disable the live TUI (also off with --ci or a pipe) |
--web |
off | Live web UI (charts + stop + final HTML report). Alias: rpcbench ui. Default bind 127.0.0.1 |
--web-host |
127.0.0.1 |
Bind for --web. Use 0.0.0.0 on a VPS to open from another machine (no auth) |
--web-port |
8765 | Port for --web (0 = ephemeral) |
--max-p95 |
SLO: max P95 latency (ms) | |
--max-error-rate |
SLO: max error rate (0–1) |
|
--max-lag |
SLO: max head lag (blocks) | |
--slo-endpoint |
Check budgets for this endpoint only | |
--out-dir |
Write report.json / report.html / report.md / metrics.prom (Action artifacts) |
|
--new-connection |
off | Fresh TCP/TLS every request. Default is keep-alive |
--http2 |
off | Prefer HTTP/2 via ALPN (falls back to 1.1). Not mixed into ranking |
--http1 |
off | Force HTTP/1.1 (default) |
--seed |
0 | Shared sequence stamp. YAML source picks (recent block, seeded address) use this |
--rank-by |
p95 |
p50, p95, p99, mean, or rps |
--similar-band |
0.10 |
Relative band on the rank key (10%). High error above this is ~, not a place |
--stale-blocks |
2 |
Head lag (blocks vs cohort median) above this is stale. Set per chain |
--block-time |
12 or known chain |
Seconds per block for estimated lag time |
--block |
cohort median | Pin the head-hash check (hex, decimal, or latest) |
--preset |
head (eth_blockNumber), chainId, or balance (eth_getBalance of the zero address) |
|
--family |
evm |
Benchmark family: evm, solana, substrate, cosmos, aptos, sui, near, starknet, bitcoin, ton, or auto (eth_chainId / getHealth / system_health / status / sui checkpoint / network_info / starknet_blockNumber / getblockchaininfo / getMasterchainInfo / ledger GET). Other families error. Not a scan |
--workload |
general when omitted |
wallet, indexer, trading, nft, tracing. Weighted mix. Omit the flag for general + short. Do not combine with --method or --preset |
--profile |
YAML mix file, or alias mix = general. Schema: Custom YAML profiles |
|
--method / --params |
Single JSON-RPC method and JSON array of params. Head probe: --method eth_blockNumber. Do not combine --method with --preset |
|
--allow-writes |
off | Required for write methods (eth_send*, personal_*, …) |
--verbose |
off | Full CLI report (Comparison, Reliability, Signals, Coverage, Timing, Tags, Burst, Providers, per-sample). Batch, Concurrency, Throughput, Logs range, simulate Methods, Archive, History, WebSocket, and Yellowstone are already in the compact report when those flags are set. Replay prints body diffs |
--json / -o FILE |
JSON to stdout, and/or write JSON to a file (table still prints unless --json, --md, --csv, or --prometheus) |
|
--html |
off | Standalone HTML to -o FILE (inline CSS/SVG, heatmap, signals, print CSS). Table still prints unless --json |
--md |
off | GitHub-flavored markdown (ranking + match; Transport, Batch, Concurrency, Throughput, Logs range, Archive, History, Trace, Debug, WebSocket, Yellowstone when measured). -o FILE writes the same markdown |
--csv |
off | Flat CSV (one row per provider; run, rank, latency, verdict, transport, batch, inflight, throughput, logs-range, archive, history, trace, debug, websocket, yellowstone). -o FILE or -o report.csv writes CSV |
--prometheus |
off | Prometheus textfile (latency, error rate, rps, reliability, histogram). -o metrics.prom or --out-dir writes metrics.prom. Not a /metrics server. docs/PROMETHEUS.md |
--history DIR |
Append a JSON snapshot to DIR after the run | |
--sequential |
off | Run endpoints back-to-back instead of paired |
record -o FILE |
Write a JSONL capture of the mix (method + params per line) |
|
replay --from FILE |
Lockstep replay; compare status, JSON-RPC error, and canonicalized body |
Safety
Kill switch: RPCBENCH_DISABLED=1, or create ~/.config/rpcbench/DISABLED (override path with RPCBENCH_DISABLE_FILE). RPCBench never prompts for a private key. Label the measurement machine with RPCBENCH_VANTAGE (default: hostname); optional RPCBENCH_REGION, RPCBENCH_CITY, RPCBENCH_ASN enrich the watermark. One compare is one vantage — not browser RUM. Merge regional JSON with rpcbench merge (docs/METHODOLOGY.md).
License
Metadata
Release files for rpcbench 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rpcbench-0.6.1.tar.gz | 291.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rpcbench-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 455.0 kB
Release files / rpcbench-0.6.1.tar.gz
| Download URL | rpcbench-0.6.1.tar.gz |
|---|---|
| Size | 291.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0edd2f191ac6a6ca19328e65f7b909726c1a027f690c97562143783ae36cc9f7
|
|
BLAKE2b-256 checksum How to use checksums |
f0e448296340965525f9727af56f95550a6fb2c20ddd81e0a014a0b3723296ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / rpcbench-0.6.1-py3-none-any.whl
| Download URL | rpcbench-0.6.1-py3-none-any.whl |
|---|---|
| Size | 163.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4c89727ecbacdfc32e559ef29b6cc67a89cc043a37ad2bef26ce17c26e0dc21b
|
|
BLAKE2b-256 checksum How to use checksums |
4d4494ab94986e1b71a010fcc896fa8f83359bee39824f9c7eb8bdb718785a8d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency log