guide10 min read

Arbitrum Mainnet RPC: What to Check Before You Send Traffic

Before you point production traffic at an Arbitrum One mainnet RPC, check chain ID 42161, Nitro soft confirmations vs safe/finalized, and public vs dedicated endpoints.

By RPC Directory•
#Arbitrum#Arbitrum One#Nitro#RPC#Chain ID 42161#Sequencer#Finality
$arbitrum-mainnet-rpc.md

An Arbitrum URL that returns eth_blockNumber is not a production endpoint. It is a node that answered one method. Before you point an indexer, a keeper, a bridge worker, or a user-facing app at it, check four things on that exact URL: chain ID 42161, whether you are talking to Arbitrum One (not Nova or an Orbit chain), what latest / safe / finalized mean on Nitro, and whether the node is full or archive for the calls you make. Those checks are cheap. A wrong chain ID or a soft-confirmed receipt treated as irreversible is not.

This is not a ranking of providers. Use the Arbitrum Mainnet hub to compare public endpoints and paid rows, then probe the one you intend to ship. Hub title for that page is already tuned for mainnet RPC discovery; this article is the operator runbook behind it.

Confirm chain ID 42161 before the first transaction

Arbitrum One mainnet is chain ID 42161 (0xa4b1). Arbitrum Sepolia is 421614. Arbitrum Nova is 42170 and is a different product (AnyTrust data availability), not a rename of One. Orbit chains use their own chain IDs. A URL labeled "Arbitrum" can still be Nova, Sepolia, an Orbit deployment, a fork, or a stale proxy.

Call eth_chainId on the endpoint you will configure, not on a different host from the same vendor. Expect 0xa4b1. If you also call net_version, it should agree. Do this in the same process that will sign transactions, after any fallback or load balancer, so you are not checking a different backend than production.

A wallet or SDK that already has a chain object is not a substitute. The SDK can be on 42161 while the HTTP transport still points at another URL. Read the chain ID from the transport.

Native gas is ETH with 18 decimals. Explorer for One mainnet tx hashes is Arbiscan. Pointing users or ops runbooks at etherscan.io (L1) or a Nova explorer for One hashes is a support bug.

One is not Nova, and Nitro is not OP Stack

This directory's Arbitrum mainnet hub is Arbitrum One (/chains/arbitrum/mainnet). Do not reuse a Nova URL, an Orbit RPC, or an Optimism / Base / opBNB checklist as if they were the same stack:

  • Nova (42170) is AnyTrust. Data availability and operator assumptions differ from One. A probe that passes on One does not prove Nova, and the reverse is also true.
  • Orbit chains inherit Nitro tooling with their own chain IDs and often their own sequencers. Hard-coding One contract addresses or One eth_chainId expectations into an Orbit config is a funds bug.
  • Optimism / Base / opBNB are OP Stack. Their sequencer vs replica language and Flashblocks/pending behavior are not Arbitrum Nitro. This guide is for One on Nitro with Ethereum as parent. For OP Stack mainnet operator checks, use that chain's own hub and article when you have one — do not paste OP Stack confirmation policy onto 42161 without measuring.

Official docs still publish a public HTTP example such as https://arb1.arbitrum.io/rpc. Treat published URLs as starting points. Always re-probe before baking one into a release. The hub lists current public and paid rows for Arbitrum mainnet; use those rows, not a screenshot from last quarter.

Soft confirmation vs safe vs finalized on Nitro

Arbitrum Nitro exposes the same JSON-RPC block tags Ethereum uses, with rollup-specific meaning documented by Offchain Labs:

  • latest (soft confirmation): the tip the Sequencer has ordered and executed. Sub-second UX is the point. It can still reorg if the Sequencer reorganizes unsafe blocks or if you are reading a lagging replica. Fine for tip UX, quotes, and "did my tx land in the Sequencer stream?" checks. Not enough if your app treats the receipt as irreversible — exchanges and settlement flows should not credit on latest alone.
  • safe: the batch containing the block has been posted to the parent chain (Ethereum) and reached the parent chain's safe head. Resistant to ordinary Sequencer soft reorgs; still exposed to a deep parent-chain reorg.
  • finalized: the batch has reached the parent chain's finalized head. The appropriate tag when the app must not unwind under L1 finality assumptions (credits, accounting, cross-chain messages that key off L1 finality).

Those are not informal synonyms for "wait N L2 blocks." Soft confirmation is fast and reversible. Hard finality on One is inherited from Ethereum batch posting, not from counting Arbitrum block height. Call each tag on your endpoint and log the block numbers:

  1. eth_getBlockByNumber with latest, safe, and finalized (full txs false is enough).
  2. Confirm safe and finalized do not error if your policy depends on them. On Arbitrum One they should be available; if they error, the node or proxy is not giving you Nitro finality tags.
  3. Confirm the heights move over time and that finalized ≤ safe ≤ latest under normal conditions. If safe equals latest forever, measure whether you are on a misconfigured proxy or a non-One endpoint.

Deposit and withdrawal flows still depend on Ethereum L1 for the parent side and, for L2→L1 messages, on assertion / challenge-period settlement beyond the RPC block tags. An L2 finalized tag is not a substitute for the bridge and outbox steps your integration docs require. Keep an L1 RPC policy next to the L2 one. Cross-chain workers: one confirmation policy per chain ID, stored next to each RPC URL.

Practical split:

  • Tip-of-chain UX, quotes, and Sequencer-speed reads: latest, and retry on reorg.
  • "Posted and parent-safe": safe only after you have seen it advance on this node.
  • Irreversible for credits and accounting: finalized.
  • Cross-chain workers: one confirmation policy per chain ID, stored next to the RPC URL.

Full node vs archive: decide from the calls, not the label

A full Arbitrum (Nitro) node serves recent state and typically retains chain data — blocks, receipts, and logs — even when it prunes historical state. That is enough for eth_call against the latest block, eth_sendRawTransaction, receipts, and eth_getLogs backfills over the ranges the node still stores. It is not enough if you need state at an old block.

You need archive access when the workload does any of these:

  • eth_call or eth_getBalance at a historical block, not just latest
  • Reconstructing a position, allowance, or vault from an old block hash
  • debug_ or trace_ against older transactions
  • An indexer that backfills by executing state-dependent calls or traces at old blocks (not merely replaying blocks, receipts, or logs from genesis)

"Archive" on a pricing page is a claim. Confirm it with a call you actually make. Pick an old block (a known height from months ago, not latest minus 20), run eth_getBalance or eth_call at that height, and keep the error. A pruned node usually returns a missing-state or header error. A timeout is not the same as "not archive." Retry once, then treat a consistent historical failure as pruned.

Do not assume the public endpoints on the hub are archive. Most shared public nodes are not. If the product only needs the tip of the chain, say so and stay on a full node. Archive is extra disk and extra cost. Pay for it when the queries require it.

Log filters are a separate limit. eth_getLogs on Arbitrum is often capped by block range, topic count, or timeout, and some public endpoints reject wide filters. Probe the method on the URL you will use, with a range your indexer actually requests. The eth_getLogs caps guide covers the failure modes across EVM L2s including Arbitrum. A node that is "archive" can still refuse a wide filter.

WebSockets: subscribe only after the socket survives a restart

HTTPS is the default for reads and for eth_sendRawTransaction. Use a WebSocket when you need eth_subscribe (new heads, logs, pending transactions) and you have measured the connection. Sequencer feed latency is not the same as a third-party wss that reconnects slowly.

Before you flip the transport:

  • Open the wss:// URL, not a guessed port on the HTTP host.
  • Subscribe to newHeads and confirm events arrive.
  • Kill the connection and confirm your client reconnects without duplicating side effects.
  • Check whether the provider counts WebSocket traffic against the same quota as HTTP. Some do. Some drop idle sockets.

A public WebSocket is fine for a local script. It is a poor primary feed for a keeper that must see every head. If the socket drops, you miss blocks until reconnect. For anything that settles money, keep an HTTP poll of eth_getBlockByNumber with finalized (or safe, if that is your documented policy) as a backstop, even if the socket is the fast path.

Probe the endpoint you will ship, then look at paid rows

Run this set once against the candidate URL and save the responses. You want a record, not a vibe.

  1. eth_chainId returns 0xa4b1.
  2. eth_blockNumber moves when you poll twice a few seconds apart.
  3. eth_getBlockByNumber with latest, safe, and finalized returns three heights you can explain. safe and finalized must not error if your app depends on them.
  4. One balance or receipt read against a known Arbitrum One mainnet address (not Ethereum, Nova, or Sepolia).
  5. One historical eth_call or eth_getBalance at an old block, if you need archive.
  6. One eth_getLogs filter in the range your indexer uses.
  7. If you need push: a wss newHeads subscription that survives a disconnect.

Public endpoints on the hub are shared and rate-limited. Use them for the probe and for development. For production, compare the paid rows on the same hub for archive, WebSocket, and method coverage. Dwellir is one of those rows: a flat response credit rather than a compute-unit table, which is easier to budget when the mix of eth_call, logs, and traces is not stable. It is not the only option, and a cheaper URL that fails the probe above is not a savings. The public vs paid guide covers when a shared endpoint stops being enough. The pricing calculator is there for the rest.

Practical split for an Arbitrum One stack

  • User-facing / Sequencer-speed path — chain ID 42161 / 0xa4b1, RPC you probed today, confirmation policy documented as latest (soft confirmation) when that is intentional.
  • Settlement / accounting path — same chain ID, prefer safe or finalized explicitly; do not reuse the tip URL's mental model without the tag.
  • Ethereum L1 path — separate config for deposits, proofs, and withdrawal finality; never point L1 calls at the Arbitrum URL.
  • Backend / indexer — dedicated or paid endpoint with explicit method and archive needs; do not share the browser or mobile client's public URL.
  • Receipt policy — write down whether "confirmed" means latest, safe, or finalized on Arbitrum One, and store that next to the RPC URL.

If the probe fails, do not ship the URL because the marketing name said Arbitrum. Change endpoint, or change the call, until chain ID, One-vs-Nova, soft vs hard finality, and node mode match the job.