Skip to content

Free-tier maxxing

About 110 requests/second of Solana RPC, for $0/month — and more if you want it. No single free tier gets you there; the best one on its own is 25 req/s. Stacking nine endpoints behind one local address does, because no single key ever sees more than a fraction of your traffic.

This page is the arithmetic and the config.


Is there a free Solana RPC endpoint?

Yes, several, at three different levels of commitment.

No signup at all. Two endpoints need no account and no key:

https://api.mainnet-beta.solana.com     # Solana Foundation
https://solana-rpc.publicnode.com       # PublicNode, operated by Allnodes

The Solana Foundation endpoint is documented at roughly 10 req/s (100 requests per 10 seconds per IP), 4 req/s for any single method, 40 concurrent connections, and 100 MB per 30 seconds. Solana's own cluster documentation says plainly: "The public RPC endpoints are not intended for production applications."

PublicNode publishes no rate limits at all, which cuts both ways: there is no documented cap to design against, and no commitment that today's throughput survives tomorrow. Verified serving mainnet 2026-09-05. Treat it as free capacity of unknown size — start it with a conservative max_rps, watch rpc_plane_rate_limited_total and the 429 rate, and raise the cap until one of them moves.

A third, https://public.rpc.solanavibestation.com, also needs no key, but it is too small to stack. It publishes no limit, and when measured on 2026-11-06 it held about 1 req/s, a tenth of the smallest leg below, so it is left out of the config. The free key from the same provider is in.

Free tiers with an account. Seven providers give you real capacity for a signup and no card:

Provider Monthly allowance Rate limit Notes
Alchemy 30M compute units 25 req/s ~3M standard reads. Largest on dedicated infrastructure
Chainstack 3M request units 25 req/s 1 RU per call, so 3M calls. Renews monthly
dRPC 210M compute units / 30 days unpublished ~10.5M calls — the largest allowance here, but served over dRPC's public node pool
OnFinality 400K response units per day 40 RU/s → 20 calls/s 2 RU per call, so ~200K calls/day (~6M/month). Note the allowance is daily
QuickNode 10M credits (trial) 15 req/s Does not sell overage; the endpoint stops when credits run out
Helius 1M credits 10 req/s Only 1 sendTransaction/s — the binding limit if you write
Solana Vibe Station none — unmetered 5 req/s Flat-rate provider, so there is no allowance to exhaust; the rate is the only ceiling. 1 sendTransaction/s

Two of those need reading carefully. OnFinality's cap is in response units, not requests — every Solana method costs 2 RU, so 40 RU/s is 20 calls/s. And dRPC's free tier runs on its public node pool, which is slower and less reliable than its paid endpoints; it belongs in the stack as headroom, not as the leg you route latency-sensitive traffic to.

Free forever? Alchemy, Chainstack, Helius and dRPC renew (dRPC every 30 days from signup); OnFinality renews daily. Solana Vibe Station has nothing to renew, because it meters nothing. QuickNode's is a trial — 10M credits once, not 10M a month. Treat QuickNode as the leg that disappears. The two no-signup endpoints have no allowance to exhaust, only unpublished (Foundation: published) throughput ceilings.

Alchemy, QuickNode and Helius verified 2026-09-05; Chainstack, dRPC and OnFinality 2026-09-06; Solana Vibe Station 2026-11-06, with its limits also measured. Rates and weights for all of them are in provider pricing.


What stacking actually buys

Adding the caps:

Rate Monthly allowance
Alchemy free 25 req/s 30M CU (~3M reads)
Chainstack free 25 req/s 3M RU (3M reads)
OnFinality free 20 calls/s 400K RU/day (~6M reads)
QuickNode trial 15 req/s 10M credits (~333k reads)
Helius free 10 req/s 1M credits (~1M reads)
Solana Foundation 10 req/s unmetered, no SLA
Solana Vibe Station free 5 req/s unmetered, not for production
Subtotal ~110 req/s ~13.3M metered reads, plus two unmetered legs
dRPC free unpublished 210M CU/30d (~10.5M calls), public pool
PublicNode unpublished unmetered, no SLA

dRPC and PublicNode are left out of the subtotal deliberately — neither publishes a rate limit, so adding a number would be inventing one. In practice both are extra headroom on top of the 110, and dRPC's 10.5M calls would nearly double the monthly allowance if you use it.

Three honest caveats. Rate limit and monthly allowance are separate ceilings — you can be well under 110 req/s and still exhaust Helius's 1M credits in a day of steady traffic. A sustained 110 req/s burns roughly 285M calls a month, far past every allowance here; the stack is sized for bursts and development traffic, not a constant load. And the proxy's own health probes draw on the same allowances, around the clock. At the probe interval used below they take about 1.5M of the 13.3M reads; at a 2-second interval they would take 11M of them before you sent a single request (see why it works).

Writes are the real constraint. Helius allows 1 sendTransaction/s on free, and the public endpoint is not somewhere to land transactions. If you are writing more than a couple of transactions a second, a paid provider is the answer, not more free keys.


Config

[health]
interval_ms             = 30000  # every probe is 2 billed getSlot calls per key; see "Why it works"
window_secs             = 30
circuit_open_failures   = 3      # hard failures in a row; a 429 never opens a circuit
circuit_error_threshold = 0.4
circuit_cooldown_secs   = 2      # a failed key gets back on trial at the first probe after this

[routing]
strategy    = "weighted_random"  # spread load across all providers
max_retries = 8                  # try all nine before giving up

[[providers]]
name    = "alchemy"
url     = "https://solana-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
weight  = 5      # weights track capacity: 25 req/s is the biggest share
max_rps = 25

[[providers]]
name    = "chainstack"
url     = "${CHAINSTACK_URL}"   # copy your node endpoint from the Chainstack console
weight  = 5
max_rps = 25

[[providers]]
name    = "onfinality"
url     = "${ONFINALITY_URL}"   # copy your endpoint from the OnFinality dashboard
weight  = 4
max_rps = 20   # 40 RU/s at 2 RU per call

[[providers]]
name    = "quicknode"
url     = "https://your-endpoint.solana-mainnet.quiknode.pro/${QUICKNODE_API_KEY}/"
weight  = 3
max_rps = 15

[[providers]]
name    = "helius"
url     = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
weight  = 2
max_rps = 10

[[providers]]
name    = "solana-foundation"
url     = "https://api.mainnet-beta.solana.com"
weight  = 2
max_rps = 10

[[providers]]
name    = "publicnode"
url     = "https://solana-rpc.publicnode.com"
weight  = 2
max_rps = 10   # no published limit — start conservative and raise it on evidence
history = "recent"   # keeps about a week of history; see "Not every free endpoint keeps old history"

[[providers]]
name    = "drpc"
url     = "${DRPC_URL}"   # copy your endpoint from the dRPC dashboard
weight  = 2
max_rps = 10   # no published free-tier limit, and it runs on the public pool — stay conservative

[[providers]]
name    = "solana-vibe-station"
url     = "https://free.rpc.solanavibestation.com/?api_key=${SVS_API_KEY}"
weight  = 1
max_rps = 5
history = "recent"   # ~11 h of getSignaturesForAddress, and a gap in getTransaction
export ALCHEMY_API_KEY=...
export QUICKNODE_API_KEY=...
export HELIUS_API_KEY=...
export CHAINSTACK_URL=...     # full endpoint URL, key included
export ONFINALITY_URL=...
export DRPC_URL=...
export SVS_API_KEY=...
rpc-plane -c free-tier.toml run

A bundled version ships as examples/free-tier.toml.


Why it works

Weights track capacity, not provider count. Equal weights send Helius the same share as Alchemy, so Helius hits its 10 req/s cap while Alchemy sits idle at 40% utilisation — and the stack sheds load long before it needs to. Setting weight in proportion to each cap (5/5/4/3/2/2/2/2/1 for 25/25/20/15/10/10/10/10/5) keeps every provider at roughly the same percentage of its own limit.

weighted_random over failover_ordered. failover_ordered sends everything to the first provider and moves down the list only when that one fails or reaches its cap. That burns one monthly allowance at a time. weighted_random spreads from the first request.

max_rps makes it proactive. A 429 doesn't open a circuit, since the key is healthy, only capped. It demotes the key's score, so weighted_random sends it less, but only after the key has already refused requests. A max_rps cap sheds load before the 429s start. Set each one to the limit the plan actually publishes. It is a load-shedding hint, not a hard throttle: if every key is momentarily at its cap the proxy forwards anyway rather than dropping the request.

interval_ms = 30000, because the probe is billed too. Every interval the proxy probes each provider with two getSlot calls, one each at processed and finalized (the default probe_commitments). They are ordinary billed calls, they don't draw from max_rps, and they run whether or not you send any traffic. At a 2-second interval that is 2.6M calls per key per month, enough to use up several of these allowances on its own:

Free allowance getSlot costs Probes at 2 s Probes at 30 s
Helius, 1M credits/month 1 credit gone in 12 days 17%
QuickNode, 10M-credit trial (once) 30 credits gone in 4 days gone in 58 days
Alchemy, 30M CU/month 20 CU gone in 17 days 12%
Chainstack, 3M RU/month 1 RU 86% 6%
OnFinality, 400K RU/day 2 RU 43% 3%
dRPC, 210M CU/30 days 20 CU 25% 1.6%

That assumes the proxy runs all month. Solana Vibe Station, the Solana Foundation and PublicNode meter nothing, so there the probe only costs rate, 0.07 req/s per key at 30 seconds.

What a longer interval gives up is how fresh the health readings are. Slot drift and latency come from the probe, so a key that falls behind is noticed within 30 seconds rather than 2. Failures are still noticed at once: live requests feed the circuit breaker too, and with max_retries covering every leg a failed request is retried elsewhere. For light traffic, 60000 halves the probe cost again.

circuit_cooldown_secs = 2. A circuit opens only on hard failures (errors and timeouts), never on a 429. Once the cooldown has passed, the next probe puts the key back in rotation on trial, and the first live success closes the circuit. The 30-second default would add up to another 30 seconds to that.

max_retries = 8. With nine providers, N - 1 means every one is tried before the proxy gives up. Drop it to match if you leave any leg out — the no-signup and unpublished-limit legs (PublicNode, dRPC) are the easiest to drop first.


The per-method limits are tighter than the headline

This is the part that catches people. Providers publish one number in the marketing table and several smaller ones underneath:

Headline sendTransaction DAS getProgramAccounts
Helius free 10 req/s 1/s 2/s 5/s
Solana Foundation ~10 req/s — — 4/s for any single method
Chainstack free 25 req/s not published separately — —
Alchemy free 25 req/s not published separately — —
QuickNode trial 15 req/s not published separately — —
OnFinality free 20 calls/s not published separately — —
Solana Vibe Station free 5 req/s 1/s not served not published for Free (2/s on Lite)

Chainstack publishes its per-method caps on a different axis — not by category but by named method: getBlock 400 RPS, getBlockTime / getBlocks / getBlocksWithLimit 500 RPS, getTokenAccountsByOwner 80 RPS in most regions, and getSupply at 2 RPS on paid plans only, so on the free Developer tier getSupply is not available at all. All of those sit above the free tier's 25 req/s except getSupply, so in practice the plan limit binds first — but if you route getSupply through Chainstack on the free tier it will simply fail, and that is worth an allowlist.

max_rps is per provider, not per method — all methods draw from one bucket — so max_rps = 10 on Helius lets you spend the entire allowance on writes and take 429s at a tenth of the cap.

The fix uses what already ships. max_rps is per provider entry, and only name has to be unique, so register the same key twice:

# Writes get their own 1 rps bucket.
[[providers]]
name    = "helius-writes"
url     = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
methods = ["sendTransaction"]
weight  = 1
max_rps = 1

# Everything else uses the general limit.
[[providers]]
name    = "helius-reads"
url     = "https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}"
weight  = 2
max_rps = 10

Two independent buckets: writes cannot eat the read budget, and rpc_plane_rate_limited_total{provider="helius-writes"} tells you exactly which limit is binding. The write entry excludes getSlot, so it skips the health probe and is scored from live request outcomes instead — see submission-only providers and working around a per-method limit.

Worth doing for any provider whose write cap you actually care about. It is the difference between 1 write/s working reliably and 1 write/s working until your read traffic spikes.


Keep the history reads off the small tiers

The monthly allowances above assume standard reads. History calls do not cost the same:

Standard read getBlock / getTransaction
Helius 1 credit 10 credits
Alchemy 10 CU 40 CU
QuickNode 30 credits 30 credits (no premium)

A getBlock-heavy workload drains Helius's 1M credits ten times faster than the headline figure suggests. If you are backfilling history on free tiers, send those calls to QuickNode — it charges no archival premium — using a methods allowlist, and let the others take the account reads. See the pricing reference for the full tables.


Not every free endpoint keeps old history

Cost is one problem with history on free endpoints. Depth is the other: some keep only the last few days, and they don't return an error for anything older. Checked on 2026-10-27, PublicNode answered getTransaction with null for a transaction 7 days old, where the Solana Foundation endpoint still served one from 180 days back. Its getSignaturesForAddress returned 87 of an address's latest 1000 signatures, also without an error.

Behind a proxy that makes a pool with one shallow leg unreliable for history. Under weighted_random, every lookup for an older transaction that lands on the shallow leg comes back as "transaction not found". Under parallel_race it is worse: the null is cheap to produce, so the shallow leg is usually the fastest.

Solana Vibe Station's free key goes further back, but it fails in two ways that don't return an error either. Checked on 2026-11-06, its getSignaturesForAddress covered only about the last 11 hours: 236 of an address's latest 1000 signatures. And getTransaction, which otherwise served transactions back to October 2024, returned null for 49 of 76 sampled transactions from 31 August to 21 October 2026. The details are on its setup page.

That is what history = "recent" on the PublicNode and Solana Vibe Station entries above is for:

  • Its null or error answer to getTransaction or getBlock fails over to another provider, so a lookup outside its window costs one extra round trip instead of a wrong answer.
  • getSignaturesForAddress is never sent to it, since a truncated list gives nothing to fail over on.
  • Misses show up as rpc_plane_requests_total{status="miss"} and don't count against its health.

Check any other free key the same way before trusting it with history: call getTransaction for a signature a few months old. null, where another provider returns the transaction, means it belongs in recent. See history lookups for the full behaviour.


When to upgrade

This is a development, side-project and non-critical-traffic setup. Free endpoints are rate limited and carry no SLA.

The signal to move is not usually throughput — it is one of these:

  • You need more than ~1 write/second. Helius and Solana Vibe Station both cap sendTransaction at 1/s on their free tiers, and neither public endpoint is somewhere to land transactions.
  • You are exhausting a monthly allowance before the month ends.
  • QuickNode's trial credits ran out and the stack quietly dropped to ~95 req/s.
  • You need a rate limit you can design against — neither no-signup endpoint publishes one.
  • You need someone to call when it breaks.

At that point add one paid provider as primary and keep the free stack as failover with failover_ordered — the free keys cost nothing to leave in place, and they are a genuinely useful backstop when the paid provider has a bad day.