Babylon RPC: A Developer's Guide (bbn-1, 9.6s Blocks, and a Pruning Floor That Moves)

By John Sullivan · September 22, 2026 · 8 min read · #babylon #rpc #developer guide #cosmos #cometbft

Babylon is the chain where Bitcoin holders stake BTC natively — no bridge, no wrapper — to secure proof-of-stake networks, and it is not an EVM chain: no eth_* methods, no numeric chain ID, and a REST/gRPC surface that matters as much as the RPC one. Most integration pain here comes from expecting the EVM habits, and from one thing that genuinely surprised me when I measured it: the pruning floor is not a single number.

Everything below was measured on 2026-09-22 through https://rpc.swiftnodes.io/rpc/babylon and the public LCD, from inside a datacenter so the latency figures are not a residential round trip. Re-run any of it.

The essentials

Network / chain ID bbn-1 (a string; there is no numeric chain ID)
Consensus client CometBFT 0.38.22 (protocol p2p 8, block 11, app 0)
Block time ~9.58 s (stable over 100, 1,000 and 5,000-block windows)
Head at time of writing 4,580,805
Active validators 68, of max_validators: 100
Unbonding period 1,814,400 s = 21 days
Block limits max_bytes 22,020,096 (~21 MiB), max_gas 300,000,000
EVM methods None. eth_chainId, eth_blockNumber, eth_getLogs, web3_clientVersion-32601 Method not found
RPC https://rpc.swiftnodes.io/rpc/babylon?key=YOUR_API_KEY
Also exposed gRPC and REST/LCD — for Babylon these are not optional extras

Connecting

JSON-RPC 2.0 over POST, method in the body, and note the object params ({"height": "..."}), not Ethereum's array.

curl -s -X POST https://rpc.swiftnodes.io/rpc/babylon?key=YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"status","params":{}}'
const RPC = "https://rpc.swiftnodes.io/rpc/babylon?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);   // 4,580,805 at time of writing
const block  = await rpc("block", { height: String(height) });
console.log(block.block.header.chain_id);                  // "bbn-1"

Heights are strings. {"height": 4580805} as a JSON number is rejected, which is the single most common reason a working Ethereum client breaks on a Cosmos chain.

CometBFT's own REST-style paths (GET /status, /block?height=N) do not work through a load-balanced RPC path — POST only. And errors arrive as a JSON-RPC error object with HTTP 200, so a client that only checks the status code will read "no data" instead of "refused".

The 9.6 second block is the design, not a problem

I checked this three ways because a single window has misled me before on a Cosmos chain:

Window Measured
last 100 blocks 9.62 s
last 1,000 blocks 9.57 s
last 5,000 blocks 9.58 s
whole life (4,580,805 blocks since the April 2025 launch) ~10.0 s

Consistent. Babylon is a slow-block chain on purpose — it is coordinating Bitcoin-signature verification, not racing for throughput. Two consequences worth designing for: do not use "blocks since X" as a proxy for time, and do not copy a 1-second-chain timeout budget. Anything waiting on a Babylon block should budget tens of seconds, and a max_age_num_blocks of 100,000 is roughly 11.3 days of chain history, not a few hours.

What is actually in a Babylon block

The block I sampled at 4,580,800 held 23 transactions, and decoding them gave one message type: MsgAddFinalitySig. That is the chain working — finality providers attesting to the state of consumer chains. Two things follow:

  • Block "activity" on Babylon is mostly protocol traffic. Counting transactions per block tells you almost nothing about user demand here.
  • block_results.txs_results is where the useful per-transaction detail lives: each of the 23 txs I sampled reported ordinary Cosmos gas (gas_wanted 126,916 / gas_used 116,127) and 12 events each. And those per-tx events are easy to miss, because the top-level events array on the same response was empty, as were begin_block/end_block — on this chain the events hang off individual results.

Which is the practical version of the same rule: block and block_results are two separate calls. A poller that fetches only block sees 23 transactions and zero events, and concludes — wrongly — that nothing happened.

Also, like every CometBFT 0.38 chain I have measured recently: the header contains no num_txs and no total_txs — 0.38 removed them. Read block.data.txs.length. Code ported from 0.37 gets undefined, which becomes NaN in arithmetic and a silently-empty loop in anything that filters "skip empty blocks".

The pruning floor gave three different answers in six calls

This is the finding to plan around. Requesting block at height 1 six times, alternating between the pool's upstream nodes:

height 1        -> lowest height is 2694820
height 1        -> lowest height is 3017244
height 1000     -> lowest height is 2694820
height 1000     -> lowest height is 2556426
height 1000000  -> lowest height is 3017244
height 1000000  -> lowest height is 2694820

Three distinct floors — 2,556,426, 2,694,820 and 3,017,244 — for the same URL. Each is a different node in the pool, each pruning on its own schedule. Heights at and above ~2.6M resolved fine (2,600,000 → 2026-02-03, 4,000,000 → 2026-07-18); anything older fails with an explicit refusal, which is at least the honest failure mode.

So:

  • "How far back can I read?" has no stable answer on a pooled RPC. Ask the node at startup — request height 1 and read the error — and treat the highest number you see as the safe floor.
  • Never cache a historical query's success as proof of retention. One success means one node had it.
  • Babylon is not addressable as an archive target here (chain_id is null in our own config and no archive flag is set on the HTTP rows), so plan around the ~2.5M+ window, not from genesis. If you need genesis-to-now history, you need an indexer or an archival node you run yourself.

tx_search refuses to search

Query Result
tx.height=4580802 113 ms, total_count=23
tx.height>0 16,095 ms, Backend node error
message.action='/cosmos.bank.v1beta1.MsgSend' 28,082 ms, Backend node error
tm.event='Tx' accepted, total_count=0

Exact-height lookup is fast. Ranges and attribute queries burn 16–28 seconds and then error rather than returning a partial answer — the indexer here is not built for them. And tm.event='Tx' is a websocket event query, so through plain HTTP RPC it returns an empty set rather than an error, which is easy to misread as "no transactions match".

If you need "transactions matching X over a period", iterate the heights you care about, or use the LCD's module endpoints. Treat tx_search as a by-height lookup, not a search index.

The LCD is half the API

The RPC gives you blocks and consensus. Babylon's actual domain — BTC staking — is on REST/gRPC, and it answers fast:

# Covenant keys, slashing params, staking thresholds — the BTC-staking rulebook
curl -s https://babylon-rest.publicnode.com/babylon/btcstaking/v1/params

# Unbonding time (21 days) and the 100-validator cap
curl -s https://babylon-rest.publicnode.com/cosmos/staking/v1beta1/params

# Total supply of the base denom; BABY has 6 decimals, so 1 BABY = 1,000,000 ubbn
curl -s 'https://babylon-rest.publicnode.com/cosmos/bank/v1beta1/supply/by_denom?denom=ubbn'

All three returned HTTP 200 in my pass, including the babylon/btcstaking/v1/params path with the covenant public keys that make Babylon's slashing scripts work. If you are building on Babylon staking state, the LCD is the right tool and the RPC is the wrong one — and SwiftNodes carries REST rows alongside RPC for this chain for that reason.

Websocket: what I could and could not verify

I want to be precise here rather than repeat a field in a status page. wss://babylon-rpc.publicnode.com/websocket completed the handshake, answered health, and acknowledged a subscribe to tm.event='NewBlockEvent' — and then delivered no event frames in 40 seconds on a chain that produces a block every ~9.6 seconds, so it should have delivered roughly four. And through our own public path, a WebSocket upgrade on /rpc/babylon currently comes back as a 301 to a docs page rather than a 101.

So: treat Babylon subscriptions as unavailable until proven otherwise, and build on polling at ~10 s intervals. One 40-second sample is not proof of impossibility — but it is enough to stop me recommending a surface I could not get to emit an event.

What this means for your stack

Budget for 9.6-second blocks everywhere: timeouts, staleness thresholds, retry backoff, "how long until final" UX.

Never trust a single pruning measurement. Ask for height 1, take the worst floor you observe, and expect it to differ per call.

Send heights as strings, read errors from the JSON body, and expect no eth_* at all.

Use block_results for events and the LCD for staking state, and treat tx_search as by-height only.

And the reason this is worth reading rather than guessing: every number above came from calling the endpoints, including the one that contradicted itself three times in six calls. SwiftNodes publishes Babylon RPC, gRPC and REST behind one key, and our weekly-measured method support matrix exists because claims that are not measured go stale — Babylon's page is at babylon-rpc.

The short version

Babylon Genesis is CometBFT 0.38.22 on chain bbn-1: ~9.58 s blocks (confirmed across 100/1k/5k windows and the chain's own lifetime average), 68 active validators of a 100 cap, a 21-day unbonding period, ~21 MiB blocks, and no eth_* surface whatsoever. Blocks are mostly MsgAddFinalitySig traffic, so transaction counts are protocol noise rather than demand. tx_search answers exact heights in ~113 ms and fails on ranges after 16–28 s. The header has no num_txs in 0.38, health returns an empty object, and the pruning floor is not one number — six attempts named three different floors (2.55M, 2.69M, 3.02M), because each upstream node prunes on its own schedule. Use the LCD for the BTC-staking state, budget 10 seconds per block, and treat WebSockets as unproven.

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

One key covers all 86 chains — including the Cosmos-family ones whose surface is nothing like Ethereum's — and the free tier is 2 requests per second, enough to run every probe above and check the pruning floor 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 →