Here is the trap with monitoring a blockchain node: the obvious check passes when the node is broken.
You point a standard uptime monitor at your node's RPC endpoint. It sends a request, gets back HTTP 200, and reports the node as "up." Green dashboard, no alerts. Meanwhile the node has been stuck on the same block for two hours, is still syncing and returning stale data, or is answering every call with an error object buried inside a perfectly valid-looking JSON response. Everything depending on that node is failing, and your monitoring is happy.
This guide is about closing that gap: why a status-code check is misleading for RPC endpoints, what you actually need to verify, and how to monitor a JSON-RPC or blockchain node so that "up" means usable.
A JSON-RPC endpoint, whether it's an Ethereum execution client, a Solana validator's RPC, a Bitcoin Core node, or any other chain, is an HTTP server sitting in front of the node software. A plain uptime check only talks to that HTTP layer. And the HTTP layer will cheerfully return 200 OK in all of these broken states:
error object, with an HTTP 200 around it. A status-code check never looks inside the body, so it never sees the error.In every case, the surface check ("did the server respond 200?") gives the wrong answer. The information you need is inside the JSON, and you only get it by sending a real RPC call and reading the response.
Three checks, in increasing order of how much they tell you. A good node monitor does all three.
Send a real JSON-RPC request, a POST with a proper JSON body, and confirm the response is well-formed JSON containing a result, not an error object. This alone is a big step up from a status-code check: it catches malformed responses and JSON-RPC-level errors that a 200 would hide.
A minimal Ethereum-style request to fetch the current block number looks like this:
Different chains use different methods. Solana's RPC has getSlot and getHealth, Bitcoin Core has getblockchaininfo; the shape stays the same regardless: send a real call, require a result, reject an error.
This is the check that catches a stalled node, and it's the one almost nothing does by default. A single call tells you the node responded; it can't tell you the node is keeping up. To know that, you have to compare across time: record the block height (or slot) on each check and confirm it has increased since the previous one.
Most uptime tools can't do this. They evaluate each check in isolation: they can assert that a value in the response sits above a fixed threshold, but they don't remember the previous check's value to compare against it. A height that's below a number you set gets caught; a height that's simply frozen while still above that number sails through. For a long time the only fix was a small script you wrote yourself, a cron job that stored the height and compared it on each run.
failover.io does the comparison natively. You give a monitor a tracked value path (the JSON path to the height, like result for eth_blockNumber) and a stall window in seconds. On every check the worker extracts the value, decodes hex if the chain returns it, and compares it to the last one it saw. The moment the value sits unchanged longer than the window, the check fails and the alert chain fires, even though the endpoint is still answering 200 in 30 milliseconds. When we tested this, the check at 119 seconds of stall passed and the check at 179 seconds failed against a 120-second window; the failure reason in the check log read Tracked value stalled: result = 1 unchanged for 179s. If you'd rather keep the comparison in your own code, the script-plus-heartbeat approach still works and failover.io's heartbeat monitors make a fine alert target for it.
Most clients expose their own sync state. Ethereum execution clients answer eth_syncing, which returns false when fully synced or an object describing progress when not. Solana has getHealth, and Bitcoin Core reports verification progress in getblockchaininfo. Querying the node's own view of itself catches the "up but serving stale data because it's still catching up" case directly.
| Check | Catches | Misses if skipped |
|---|---|---|
| HTTP 200 only | Server / port down | Everything below |
| Valid RPC result | JSON-RPC errors, malformed responses | Stalled & syncing nodes |
| Height vs absolute threshold | Node fallen badly behind | A frozen height still above the threshold |
| Height delta (tracked value) | Stalled node, lost consensus | — |
| Sync-state check | Node serving stale data while catching up | — |
To run the checks above, a monitoring tool has to be able to do things a basic uptime checker often can't:
GET can't make a real RPC call at all.result exists, that error does not, ideally that a value matches an expected pattern.POST with a dedicated request-body field, up to 20 custom headers for authenticated endpoints, and body assertions that fail on a JSON-RPC error object or a missing result. For the stalled-node case there's native value tracking: point it at the height field, set a stall window, and the monitor compares each check against the last and goes down when the value freezes. Presets fill in the right call and path for Bitcoin Core, EVM chains, Solana, Tendermint/Cosmos, Polkadot, XRPL and NEAR. A failing node then triggers the same escalation climb to SMS and voice call as any other outage. The free plan covers 5 monitors; start free.
Putting it together, a solid setup for a single RPC node looks like this:
eth_blockNumber, getSlot, or your chain's equivalent), including any required auth headers.result field and fails on an error object.A blockchain node can return HTTP 200 and still be useless: stalled, syncing, or returning a JSON-RPC error inside a valid-looking response. Monitoring a node properly means treating it as a correctness check, not a connectivity check: send a real RPC call, require a valid result in the body, track the block height across checks so a frozen value fails the monitor, and read the node's own sync state. That requires a monitor that can POST a custom JSON body, send auth headers, assert on the response, and remember the last value it saw, plus an escalation path so that when the node degrades at 3 a.m., a human actually finds out.
failover.io: first-class JSON-RPC checks, body assertions, and escalation that climbs until someone acknowledges. Free plan, no credit card.
Start monitoring free →