Solana RPC for validator monitoring¶
A validator operator's monitoring — the alerting loop, the metrics exporter, the stake dashboard — polls the same handful of cluster-state methods on a timer and never sends a transaction. Two things set it apart from every other workload on this site. You already run a node that answers most of it for free. And that node is the one endpoint sure to be behind right after it restarts, which is when you most want the numbers.
What this workload costs¶
Modelled on 25% getSlot, 20% getVoteAccounts, 15% getClusterNodes, 15% getEpochInfo,
10% getBalance, 5% getBlockProduction, 5% getLeaderSchedule, 5% getBlock — no writes —
computed from the pricing dataset:
| Provider | Blended $/M | At 10M calls/month |
|---|---|---|
| dRPC | $6.00 | $60 |
| Alchemy | $6.98 | $70 |
| Helius | $7.25 | $73 |
| OnFinality | $7.50 | $75 |
| Chainstack | $10.00 | $100 |
| QuickNode | $15.00 | $150 |
| Triton One | $10.00 + bandwidth | $100 + bandwidth — about $290 as measured |
No method here is expensive per call, so the spread is narrow: 2.5× from top to bottom, against 7.6× for an indexer. Two details decide the order.
Alchemy has the cheapest standard read on the board at $4.50/M and still comes second, because
getSlot and getEpochInfo are two of the methods it bills at 20 CU instead of 10, and they are
40% of this mix. Helius pays for the one call in twenty that is a getBlock: at 10 credits, that
5% of traffic is $2.50 of its $7.25.
Chainstack's getBlock is priced at 1 RU. Monitoring reads blocks at the tip, where its archive
multiplier does not apply. Rates verified 2026-09-06 against each provider's published pricing;
overage/pay-as-you-go basis. See price per method for the weights.
Three calls carry all the bytes¶
Per-call pricing hides what is unusual about this mix: it is the heaviest on this site by payload.
Measured on mainnet on 2026-11-12 against api.mainnet-beta.solana.com, in an epoch with 698 vote
accounts, 3,859 gossip nodes and 648 leaders:
| Method | Share | Uncompressed | On the wire (gzip) |
|---|---|---|---|
getLeaderSchedule |
5% | 2.94 MB | 2.95 MB |
getClusterNodes |
15% | 1.47 MB | 379 KB |
getVoteAccounts |
20% | 314 KB | 148 KB |
getBlockProduction |
5% | 35 KB | 31 KB |
| The other four | 55% | under 400 bytes each |
The leader schedule arrived gzip-encoded and no smaller. gzip -6 takes the same body to 1.08 MB,
so how far it is compressed is the provider's choice. getBlock is in the last row because
monitoring asks for it with transactionDetails: "none", to read a leader's rewards. A full block
was 3 to 5 MB.
Averaged over the mix that is 235 KB a call on the wire, where a getAccountInfo reply is about
0.5 KB. It matters in two places.
Triton One bills it. Triton charges $0.08/GB of response bandwidth on top of $10/M calls. At
235 KB a call, a million calls is 235 GB, or $18.83. Bandwidth costs nearly twice what the calls
do, and the total is about $28.83/M: last in the table above, well behind QuickNode. getLeaderSchedule alone
is 63% of those bytes. These figures use the sizes the public endpoint served, because we have not
measured Triton's own compression. With the leader schedule compressed as far as gzip takes it the
total is about $21/M, and with nothing compressed about $45/M. RPC Plane asks every provider for
compressed responses.
Your client can avoid most of it. Both fixes are in the client. RPC Plane does not cache responses; it forwards every call you send.
- Ask for one validator.
getLeaderSchedulewith anidentityfilter returned 114 KB for the busiest leader on mainnet, and less for anyone else.getBlockProductionwithidentityreturned 223 bytes, andgetVoteAccountswithvotePubkey526 bytes. - Fetch the schedule once per epoch. The leader schedule is fixed for the epoch, and an epoch
is 432,000 slots, which is a day at 200 ms.
getEpochSchedulenever changes at all.
Your own node is the cheapest provider, and it is behind after a restart¶
A node you run answers this mix for nothing. How much of it depends on how the node is started:
- A validator's own RPC port serves Agave's minimal set unless it runs with
--full-rpc-api. From this mix that isgetSlot,getVoteAccounts,getEpochInfo,getBalanceandgetLeaderSchedule: 75% of the calls.getClusterNodes,getBlockProductionandgetBlockare outside it. - An RPC node beside it, or a validator with
--full-rpc-api, serves all of it.getBlockalso needs--enable-rpc-transaction-history, and reaches only as far back as the ledger the node keeps.
The minimal case needs one more setting. A node asked for a method it does not serve answers
"method not found", which RPC Plane treats as the caller's mistake and returns without trying
anyone else. List what the node serves in methods and every
other method routes to the fallback. Then a quarter of this mix is the fallback's every month. At
10M calls that is 2.5M: $15 on dRPC, or $35 on Helius, where the getBlock share is most of it.
So the provider question here is mostly "which fallback", and the failure to design for is not the node going down. It is the node coming back. A validator restarts for every upgrade. When its RPC port returns it is still replaying: thousands of slots behind the cluster, and answering HTTP 200 the whole time. At 200 ms slots a 30-minute catch-up starts 9,000 slots back. Monitoring that reads through that node reports the cluster as it stood at the slot the node has reached, with no error to say so.
failover_ordered keeps reading from it¶
failover_ordered is the usual answer for a free primary with a
paid backup, and it has a gap here. It leaves the first provider only when that provider's circuit
opens, and a circuit opens on failures. A node that is behind does not fail. Slot drift lowers a
provider's score, and failover_ordered does not read
scores.
best_score does. With slot freshness weighted above latency, the node keeps the reads while it
is near the tip and loses them when it falls behind. We ran v0.14.0 against a simulated node and a
fallback 50 ms away on 2026-11-12, with the config below:
| Node behind by | Reads that reached the node |
|---|---|
| 0 to 15 slots | 200 of 200 |
| 20 slots (4 s) | 2 of 200 |
| 150 slots (30 s) | 1 of 200 |
| 9,000 slots (30 min) | 3 of 800 |
| Back at the tip | 200 of 200 |
Under failover_ordered the node kept 20 of 20 reads at 9,000 slots behind.
Two things to know about the best_score result:
- A few reads still reach the node. Each probe round compares providers against the highest
slot reported in that round. Until the fallback's probe answers, the node's slot is the only one
in, so for that moment it counts as the tip. The share is roughly the fallback's latency divided
by
interval_ms: about 1% here, and 6% in the same test with a fallback 300 ms away. - The node has to be the closest endpoint. Latency is what makes
best_scoreprefer the node while both are at the tip. Run the proxy on the same host or network as the node. A node further away than the fallback loses the reads to it.
What the fallback costs when nothing is wrong¶
The health probe is two getSlot calls per provider per second by default. It is billed like any
other call and sent whether or not there is traffic. On a fallback that serves nothing on a normal
day, the probe is the bill:
| Probe setting | Calls per 30 days | Helius | dRPC | Alchemy | QuickNode |
|---|---|---|---|---|---|
Default: every 1 s, processed and finalized |
5.18M | $25.92 | $31.10 | $46.66 | $77.76 |
| Every 5 s, both commitments | 1.04M | $5.18 | $6.22 | $9.33 | $15.55 |
Every 5 s, processed only |
518K | $2.59 | $3.11 | $4.67 | $7.78 |
Set that against the traffic. A node that is down or behind for 1% of the month, about seven hours, sends 100,000 calls of a 10M-call workload to the fallback. That costs $0.60 to $1.50.
At 5 s and processed only, the fallback for a full-API node can be a free-tier key. 518K probes
plus that traffic fit inside Helius's free 1M credits a month, and use about 40% of Alchemy's free
30M CU. At the default rate they do not: 5.18M calls spends Helius's allowance in under six days. See free-tier maxxing for each provider's limits.
A 5-second round is enough for this workload. A node that falls behind is noticed within one
round. A node that fails is noticed at once, because live requests feed the circuit breaker as well
as probes. finalized has trailed processed by about a slot since Alpenglow, so probing it tells
you little that processed does not when the question is whether your node has caught up.
Config¶
[health]
interval_ms = 5000 # one probe round every 5 s
probe_commitments = ["processed"] # one getSlot per provider per round
slot_drift_threshold = 150 # 30 s at 200 ms slots
w_slot = 0.4 # above latency: a stale node loses
w_latency = 0.2 # above zero: the local node wins ties
[routing]
strategy = "best_score"
# Your own node: the validator's private RPC port, or an RPC node beside it.
[[providers]]
name = "own-node"
url = "http://127.0.0.1:8899"
history = "recent" # a getBlock it cannot serve fails over
# Without --full-rpc-api: list the minimal set, the rest goes to the fallback.
# methods = [
# "getSlot", "getVoteAccounts", "getEpochInfo", "getBalance",
# "getLeaderSchedule", "getTransactionCount", "getBlockHeight",
# "getHealth", "getVersion", "getIdentity", "getGenesisHash",
# "getHighestSnapshotSlot", "getAccountInfo", "getMultipleAccounts",
# ]
# The fallback: takes the reads while the node is down or behind.
[[providers]]
name = "helius"
url = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
The defaults are w_slot = 0.2 and w_latency = 0.4. slot_drift_threshold is where the slot
score halves, so it sets how far behind the node may be before it gives up the reads. At 150 the handover came between 15 and 20 slots with a fallback
50 ms away. A slower fallback moves it later: at 300 ms the node kept the reads 60 slots behind and
had lost them by 100. By 150 slots the node has lost them whatever the fallback's latency. The
default of 10 with these weights hands over at two slots, a gap two healthy providers show all the
time.
Keep getSlot in any methods list. A provider that cannot answer it is not probed, and an
unprobed node has no slot to measure drift from. If the fallback's own probes fail, the node is
the only slot reading left and counts as the tip; an
external slot reference gives it a third opinion.
Where splitting by method does not pay¶
The per-method cheapest provider for this mix:
| Method | Share | Cheapest | $/M |
|---|---|---|---|
getSlot |
25% | Helius | $5.00 |
getVoteAccounts |
20% | Alchemy | $4.50 |
getClusterNodes |
15% | Alchemy | $4.50 |
getEpochInfo |
15% | Helius | $5.00 |
getBalance |
10% | Alchemy | $4.50 |
getBlockProduction |
5% | Alchemy | $4.50 |
getLeaderSchedule |
5% | Alchemy | $4.50 |
getBlock |
5% | dRPC | $6.00 |
Perfect method-splitting comes to $4.78/M, which is 20.4% under dRPC alone. At 10M calls a month that is $60 → $48. Twelve dollars does not pay for two more vendor relationships, and your own node undercuts all three.
RPC Plane does not route by cost
It routes by health, score, config order or weight — never by price. Any cost split is one you encode yourself. Cost-aware routing is on hold and the binary carries no pricing table.
Related¶
- Other providers — pointing RPC Plane at your own node
- Health scoring — the score formula, probes and slot drift
- Routing strategies —
best_score,failover_orderedand history lookups - Free-tier maxxing — what probes cost against each free allowance
- Price per method — the underlying rate tables