A public RPC URL that worked the day it was listed can stop working without notice. The host disappears from DNS, the provider puts it behind a key, the origin goes down, or the node behind it stops following the chain. A wallet or app that hardcoded that URL fails for every user at once, usually with an error that looks like a network problem on the user's side.
This guide uses the directory's own re-checks from 30 September to 5 October 2026. Over those five days, re-probes on the Sei, Mantle, Sei testnet, Gnosis, and Celo hubs hid 29 public URLs and kept several that looked dead at first. It is not a ranking of providers. The goal is a check you can run yourself before a URL goes into a config file.
How the directory decides a row is dead
Every candidate gets a single eth_chainId POST per try, repeated with spacing between tries. On the Gnosis and Celo re-checks that meant eight tries about 55 seconds apart, with more tries for borderline rows. The response must be the chain's exact ID in hex, such as 0x64 for Gnosis or 0xa4ec for Celo. An HTML page, an error object, a timeout, or another chain's ID all count as a failed try.
The rule applied to the tally:
- 0 successes: hidden.
- 1 success and every other try rate limited: hidden. A URL that answers once in eight is not something a client can depend on.
- About one success in three or better: kept, with the tally noted. That is intermittent rate limiting, and the URL can still serve as a fallback.
- Right chain ID, but block reads fail on most tries: hidden. A public endpoint has to serve
eth_getBlockByNumber, not onlyeth_chainId.
Between those lines is a judgment call. On 5 October, lb.routeme.sh/rpc/evm/42220 on Celo answered 3 of 16 tries and returned HTTP 429 on the other 13, so it was hidden. The same gateway's Mantle path answered 5 of 16 on 1 October and stayed listed.
Rows that pass are not endorsed by that alone. A row that returns the right chain ID can still serve stale data or gate every other method, which is why the recipe further down adds a head-freshness check and a real read.
What the re-checks found
| Re-check | Hub | Hidden | Kept after re-check |
|---|---|---|---|
| 30 Sep 2026 | Sei mainnet | 4 | None of the four recovered |
| 1 Oct 2026 | Mantle mainnet, Sei testnet | 6 and 2 | lb.routeme.sh/rpc/evm/5000 at 5 of 16 tries |
| 2 Oct 2026 | Gnosis mainnet | 13 | lb.routeme.sh/rpc/evm/100 at 3 of 8; every endpoint recommended in the Gnosis guide at 8 of 8 |
| 5 Oct 2026 | Celo mainnet, Mantle mainnet | 2 (Celo) and 2 (Mantle) | 1RPC and OnFinality on Celo; four more Celo rows passed 8 of 8 and went back into the Celo guide |
The 2 October Gnosis re-check is the cleanest sample: 13 URLs, eight tries each, and not a single success between them. The Sei mainnet re-check used three to eight tries per row, and the Mantle and Sei testnet re-check used six to sixteen.
Failure modes, grouped
The 29 hidden URLs fall into five groups. Twenty-five were hidden between 30 September and 2 October. The 5 October re-check added four: one rate-limited row, the only stale chain, and both 1RPC Mantle URLs (1rpc.io/mantle and public.1rpc.io/mantle), whose block reads sat behind a paid plan.
| Failure mode | What the probe sees | Rows | Examples |
|---|---|---|---|
| DNS gone | No response; curl: Could not resolve host | 5 | sei.therpc.io, mantle.therpc.io, gnosis.therpc.io, gnosischain-rpc.gateway.pokt.network, web3endpoints.com/gnosischain-mainnet |
| Now needs a key, plan, or account | HTTP 401 or 403, or a JSON-RPC error asking you to authenticate or upgrade | 10 | Ankr's keyless Gnosis URL (-32000 "You must authenticate your request with an API key"), 1RPC on Gnosis (-32001 usage limit), Blast API on Mantle and Gnosis (403, service discontinued), Nodies on Mantle and Gnosis (403, paid plan), 1RPC on Mantle at 1rpc.io/mantle and public.1rpc.io/mantle (block reads 403, paid plan), an Owlracle URL (401 "Origin mismatch") |
| Origin down or path removed | HTTP 404, 521, or "chain not supported" | 10 | Nodeflare on Sei and Mantle (unknown_chain), Omniatech on Mantle and Gnosis (521), BlockPI on Gnosis (521), 0xrpc.io/gno over HTTPS and WSS (404), a Blockscout archive URL (404) |
| Rate limited | HTTP 429 with JSON-RPC -32029 "public rate limit exceeded" | 3 | lb.routeme.sh/rpc/evm/1329 on Sei (1 of 8), /1328 on Sei testnet (0 of 6), /42220 on Celo (3 of 16) |
| Stale chain | Correct chain ID, but the head does not move | 1 | rpc.celocolombia.org (5 October) |
Stale chain is the clearest failure a chain ID check cannot catch. On 5 October, rpc.celocolombia.org returned 0xa4ec on all eight tries. Its latest block was block 0, timestamped 22 April 2020. eth_syncing reported currentBlock 0x0, and eth_getBalance at latest returned -32801 "no historical RPC is available". A wallet on that URL would show zero balances and wrong nonces while passing a chain ID check.
Wrong chain ID did not show up in these re-checks, but it is the cheapest failure to catch. A testnet URL listed under mainnet, or a gateway path that routes to another network, answers with a different ID. Celo Sepolia, for example, returns 0xaa044c instead of 0xa4ec. Compare the exact hex value, not just "a number came back".
Some rows come back
A failed probe is a snapshot, and so is a passing one. The 5 October re-check reversed several results from 2 October:
- 1RPC on Celo returned
-32001"You've reached the usage limit for your current plan" on 8 of 8 tries on 2 October. On 5 October it returned0xa4econ 8 of 8 with a fresh head. - OnFinality on Celo answered
eth_chainIdon 2 October but rate limited the follow-up block read. On 5 October it answered 8 of 8 with a head within a second of wall-clock time. - Stakely and Tatum on Celo each missed one try on 2 October (an HTTP 521 and an HTTP 429). Both passed 8 of 8 on 5 October, as did SwiftNodes and thirdweb.
The opposite also happens. 1RPC on Mantle (1rpc.io/mantle and public.1rpc.io/mantle) returned 0x1388 on 8 of 8 tries on 5 October, yet most follow-up eth_getBlockByNumber calls came back as HTTP 403 "requires a paid subscription plan": 5 of 8 in the first run, then 6 of 8 and 5 of 8 in a second run on both hosts. Both are now denied for failing block reads behind a paid-plan wall. A chain ID check alone would have kept them.
That is why the directory re-probes before it hides a row, and why your own check should run on a schedule, not once.
A health check to run before you hardcode a URL
Run this against the exact URL you plan to ship, from the same network your backend or users will use.
- Chain ID match.
eth_chainIdmust return the exact hex ID you expect. Treat HTML, an error object, a timeout, or another ID as a failure. - Head freshness against a reference. Call
eth_getBlockByNumberwith"latest"on the candidate and on a second endpoint from a different operator. The block numbers should be within a few blocks of each other, and the candidate's timestamp should be within a few block times of wall-clock time. Ifeth_syncingreturns an object instead offalse, the node is not ready. - One real read. Call a method your app actually uses, such as
eth_getBalanceoreth_estimateGas. Some gateways answereth_chainIdfrom cache and gate everything else. - Repeat with spacing. Eight tries about a minute apart is enough to separate a dead URL from a busy one. Zero successes means dead. One success with the rest rate limited is not usable. About one in three or better is a fallback at most, not a primary.
- Handle 429 as its own outcome. Rate limits arrive as HTTP 429 or as a JSON-RPC error inside an HTTP 200 body (
-32029,-32005, or a "limit" message). Back off with jitter and honorRetry-Afterwhen it is present. Count rate limits separately from hard failures so a busy endpoint is not mistaken for a dead one, or the reverse. - Ship a fallback. Configure at least two URLs from independent operators, fail over on errors and stale heads, and re-check the chain ID after a failover. Re-run steps 1 to 5 on a schedule and alert when a URL starts failing, before users report it.
A minimal probe for steps 1 and 2:
URL="https://forno.celo.org" # candidate
EXPECTED="0xa4ec" # Celo mainnet
for i in $(seq 1 8); do
id=$(curl -s -m 15 -X POST -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' "$URL" | jq -r '.result // .error.message // "no result"')
ts=$(curl -s -m 15 -X POST -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":2,"method":"eth_getBlockByNumber","params":["latest",false]}' "$URL" | jq -r '.result.timestamp // empty')
age=$([ -n "$ts" ] && echo $(( $(date +%s) - $(printf '%d' "$ts") ))s || echo "n/a")
echo "try $i: chainId=$id head_age=$age"
[ "$i" -lt 8 ] && sleep 55
done
A passing run prints the expected ID on every try and a head age of a few seconds. Anything else is a reason to pick another URL or keep this one as a fallback only.
Where a paid endpoint fits
Public endpoints are shared, rate limited, and run without a service level. They are fine for development, QA, and as one fallback among several. For production wallet or indexer traffic, compare the paid rows on the hub for method coverage, archive access, and quota, and run the same checks on them.
Dwellir, which builds this directory, is one of the listed paid providers and bills one response credit per call on every method, so a mix of cheap reads and heavier calls stays predictable to budget. The public vs paid guide covers when a shared endpoint stops being enough, and the pricing calculator estimates monthly cost for a given request mix.
Re-check the hubs you depend on
The hubs from these re-checks are Sei mainnet, Sei testnet, Mantle mainnet, Gnosis mainnet, and Celo mainnet. The chain guides for Gnosis, Mantle, Sei, and Celo list the endpoints that passed when each was written. Treat every row, including those, as a candidate until your own probe passes.