Neutron RPC: A Developer's Guide (neutron-1, CometBFT 0.38, No eth_*)

By John Sullivan · September 20, 2026 · 9 min read · #neutron #rpc #developer guide #cosmos #cometbft

Neutron is not an EVM chain, and the RPC reflects that: there is no eth_* surface at all, no numeric chain ID, and the request shape is CometBFT JSON-RPC rather than the Ethereum method set. None of that is surprising once you have read a Cosmos RPC reference — but each of these will break a piece of code you already have, and a few of them will not break it loudly.

Everything below was measured on 2026-09-20 through https://rpc.swiftnodes.io/rpc/neutron, against the same chain from a second independent endpoint for anything I did not want to take on trust. Re-run any of it.

The essentials

Network / chain ID neutron-1 (a string — there is no numeric chain ID)
Consensus client CometBFT 0.38.19 (protocol p2p 8, block 11, app 0)
Block time ~7.0 s measured this week, ~2.05 s over the preceding months — see below
Head at time of writing 61,612,831
Validators 11 active, 11 signatures in last_commit, ed25519
Block limits max_bytes 22,020,096 (~21 MiB), max_gas 330,000,000
EVM methods None. eth_chainId-32601 Method not found
HTTP https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY
Execution layer CosmWasm (wasmvm) contracts, not the EVM

Connecting: the setup

CometBFT RPC is JSON-RPC 2.0 over POST, with the method name in the body. There are no params arrays in the Ethereum sense — most methods take an object, and many take nothing.

curl

# node + sync state
curl -s -X POST https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"status","params":{}}'

# a specific block by height
curl -s -X POST https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"block","params":{"height":"61612831"}}'

The one thing to get right early: this path is POST-only. A GET /status — which works against a bare CometBFT node on port 26657 — returns 404 here. Clients built around the Tendermint REST-style paths will need the JSON-RPC-over-POST form instead.

A tiny fetch helper

const RPC = "https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY";

async function rpc(method, params = {}) {
  const r = await fetch(RPC, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
  });
  const j = await r.json();
  if (j.error) throw new Error(`${method}: ${j.error.message} ${j.error.data ?? ""}`.trim());
  return j.result;
}

const s = await rpc("status");
const height = Number(s.sync_info.latest_block_height);   // 61612831 at time of writing
const block  = await rpc("block", { height: String(height) });
console.log(block.block.header.chain_id);                 // "neutron-1"

Note the explicit error check. CometBFT signals most failures as a JSON-RPC error object with HTTP 200, so a client that only looks at status codes will treat "height is not available" as a successful response with a missing field.

CosmJS users: Tendermint37Client / HttpBatchLink speak this same POST-to-root JSON-RPC, so the endpoint drops in. Verify the version handshake yourself rather than assuming — this node reports CometBFT 0.38.19, and the client you pick has to tolerate that.

What's different from an EVM chain

There is no chain ID number

eth_chainId returns -32601 Method not found, and our own status API reports chainId: null for this chain. The identifier is the string neutron-1, and it lives in status.node_info.network and in every block header. Anything in your code that expects an integer — a switch (chainId) on a config map, a wallet addEthereumChain call, a chain-registry lookup keyed by number — needs a string key here.

Two header fields vanished in CometBFT 0.38

The header I read at block 61,612,809 had exactly these keys:

version, chain_id, height, time, last_block_id, last_commit_hash, data_hash,
validators_hash, next_validators_hash, consensus_hash, app_hash,
last_results_hash, evidence_hash, proposer_address

There is no num_txs and no total_txs — 0.38 removed them. Code that reads header.num_txs gets undefined, which in JavaScript means NaN in arithmetic and a silently empty loop in a "skip empty blocks" filter. The replacement is the transaction list itself: result.block.data.txs.length (the block I sampled contained 1 tx).

health returns an empty object

{"jsonrpc":"2.0","id":1,"result":{}}

No is_node_healthy field, from either this route or the upstream directly — so a readiness probe that checks result.is_node_healthy === true will read undefined and fail, and one that checks result.is_node_healthy !== false will pass on a node that is down. Use status.sync_info.catching_up === false instead, and treat a failed status call as unhealthy. status.validator_info also came back null here, so don't build on it either.

Block production is not what the docs say

This is the one I would not have believed without measuring twice.

Window Measured
last 100,000 blocks (2026-09-12 → 09-20) 7.02 s / block
last 5,000 blocks 7.001 s
last 1,000 blocks 6.995 s
last 200 blocks 6.979 s
block 56,000,000 (2026-05-10) → head 2.05 s / block

Consecutive blocks were 7.40, 1.56, 7.41, 7.72, 7.45, 7.44 s apart — so this is a steady ~7 s cadence with occasional fast blocks, not one slow block skewing an average. I repeated the same measurement against a second, independent RPC and got 6.979 s and 7.001 s for the 200- and 2000-block windows, which rules out anything about the route in between.

Read that as: Neutron's long-run rate is ~2 s, and as of this week it is producing at roughly 7 s. Anything you have tuned against a 2 s assumption — a poll interval, a "stale tip" alert threshold, a timeout budget, a UI's "synced" indicator, an ETA in blocks — is currently wrong by about 3.5×. If you have a fixed-interval poller, make it interval-based rather than block-count-based, and set the stall threshold from a measurement you take today, not from a spec.

History: the floor moves between nodes

This is the part that will bite you in production.

Requesting block at height 1 does not return an empty result — it returns an explicit refusal naming the node's own retention floor:

{"code":-32603,"message":"Internal error",
 "data":"height 1 is not available, lowest height is 55481501"}

Then, minutes later, the same request through the same URL named a different floor: 61317000. And block at 61,317,000 failed while 61,317,001 succeeded.

That is not flakiness in the transport. A pool of independent RPC nodes each prune on their own schedule, so "how far back can I read?" has no single answer on a load-balanced endpoint. Observed floors on this route today spanned 55,481,501 to 61,317,000 — a difference of ~5.8 million blocks, i.e. one node kept months more history than another.

Practical consequences:

  • A historical read may succeed and then fail on the next identical call. Design for it: retry, and if the query matters, pin it to a specific archival endpoint rather than a pooled one.
  • Do not infer "archive" from a successful old-block read. One success means one node had it.
  • ?archive=1 is an EVM routing hint on SwiftNodes; it does not apply to CometBFT chains like this one.

For reference, the genesis method reports genesis_time: 2025-11-28T11:42:18Z with initial_height: 1 — which cannot be reconciled with a chain at height 61.6M. Treat the genesis result as a consensus-parameter document (chain ID, validator set, upgrade heights), not as a reliable chain birthday. A third endpoint I probed returned no genesis at all.

The methods you'll actually use

Method What it gives you Notes
status network, CometBFT version, head height/time, catching_up your real health check
block header + txs + last_commit for one height params.height as a string
block_results events and per-tx results for a height separate call from block
blockchain compact metadata for a height range cheap way to walk history
tx one transaction by hash, with proof
tx_search find txs by query see the warning below
validators the set at a height total came back 11
consensus_params block limits, evidence rules, vote-extensions height
broadcast_tx_sync / _async / _commit submit a tx _commit waits for inclusion
abci_query raw app state (contracts, params, stores) the Cosmos equivalent of a storage read

block and block_results are two calls. Events — the thing you usually actually want — live in block_results, so a poller that fetches only block sees transactions with no results and reports nothing happened.

tx_search: some queries do not answer

Query Result
tx.height=61612805 129 ms, total_count=1
message.action='/cosmwasm.wasm.v1.MsgExecuteContract' 17,245 ms, total_count=47532
tx.height>0 28,066 ms, then Backend node error

Exact-height lookups are fast. Attribute searches are served, but slowly and at large result counts, and an unbounded range query does not return at all — it burns ~28 s and then errors. If you need "all txs matching X over a period", iterate strict heights or use an indexer; do not hand tx_search a wide range and wait.

On the dYdX route we also saw a stricter rule — tx.height>0 rejected outright with -32701 "please specify tx.height event with strict equality". Different operators configure the indexer differently, so treat tx_search as capability you must probe per endpoint, not a guarantee of the RPC spec.

WebSocket

wss is available on this chain and our status API reports wsAvailable: true. The subscription handshake is CometBFT's, not Ethereum's: method subscribe with query, e.g. {"jsonrpc":"2.0","id":1,"method":"subscribe","params":{"query":"tm.event='NewBlockEvent'"}}, and events arrive as notifications on that subscription rather than as eth_subscribe IDs. If your code already handles eth_subscribe, this is a rewrite of the subscription layer, not a config change.

What this means for your stack

Check your assumptions about time. ~7 s blocks against a 2 s baseline changes every block-count-derived number in your system: staleness thresholds, retry backoffs, "confirmations", and how long a poll interval can be before you look dead.

Stop reading num_txs. It is gone in 0.38 and its absence is silent. Use data.txs.length.

Treat health as unusable on this surface and gate readiness on status.sync_info.catching_up.

Assume history is partial. Ask the node what its floor is by requesting height 1 and reading the error, and do that at startup rather than discovering it on a user-facing query.

Verify capability by calling it. eth_chainId, tx_search ranges and old-block reads are three places where this chain told the truth only when asked directly. SwiftNodes publishes a weekly-measured method-support matrix for exactly this reason — the EVM half of it is at Method support, and the full chain list is at /chains.

The short version

Neutron speaks CometBFT 0.38.19 JSON-RPC over POST on chain neutron-1, with 11 validators, ~21 MiB blocks and a max_gas of 330,000,000. There is no numeric chain ID and no eth_* surface at all; num_txs and total_txs are gone from the header in 0.38; health returns an empty object so readiness has to come from status; block and block_results are separate calls; wide tx_search queries time out where exact-height ones answer in ~130 ms; and the pruning floor differs between nodes in the same pool (55.48M to 61.32M observed today), so historical reads are not reliable on a pooled endpoint. The number to watch: blocks measured 7.02 s over the last 100k, against a 2.05 s average across the preceding five million — a live 3.5× slowdown, confirmed against a second independent RPC.

For Neutron RPC across load-balanced independent upstreams, with HTTP, gRPC, REST and WebSocket, grab a free API key and point your client at:

https://rpc.swiftnodes.io/rpc/neutron?key=YOUR_API_KEY

One key covers all 86 chains, and the free tier is 2 requests per second — enough to run every probe above, including the block-time windows, and check this week's numbers for yourself.

J
John Sullivan
Infrastructure Writer, SwiftNodes

John Sullivan covers RPC infrastructure, node operations, and multi-chain development at SwiftNodes — what it actually takes to keep endpoints fast, fresh, and reliable across EVM and non-EVM networks.

Related posts

Try SwiftNodes free — multi-chain RPC across 86 networks, flat-rate pricing, pay by card or crypto, no KYC. Get an API key in 30 seconds →