Summary
This page shows concrete Base RPC examples you can copy: the public endpoint, chain settings for wallets, curl JSON-RPC calls, and a JavaScript snippet using viem. It also covers how to read common errors so you can tell a bad request apart from a rate-limited or misconfigured endpoint.
Most developers searching for a Base RPC example want one thing: a working endpoint and a request they can paste into a terminal right now. This page gives you that, then explains the settings and failure modes you will hit once the example moves into a real application.
Base is an OP Stack Layer 2 that settles to Ethereum, so it speaks standard EVM JSON-RPC. That means any Ethereum tooling works against it, and the examples below should look familiar if you have used Ethereum RPC before. The differences that matter are the chain ID, the explorer, and how you handle testnet versus mainnet.
Pick the right Base endpoint before you copy anything
Base has two networks you will connect to during development, and mixing them up is the most common cause of "my balance is zero" confusion. Use this table to choose.
| What you are doing | Network | Chain ID | Example endpoint | Explorer |
|---|---|---|---|---|
| Deploying or testing against real value | Base Mainnet | 8453 | https://base.api.onfinality.io/public | basescan.org |
| Building and testing before mainnet | Base Sepolia | 84532 | https://base-sepolia.api.onfinality.io/public | sepolia.basescan.org |
Both endpoints above are public OnFinality RPC URLs and accept HTTP JSON-RPC. If you need consistent throughput, WebSocket subscriptions, archive data, or trace calls, a shared public endpoint is not the right long-term choice. Compare RPC pricing and dedicated nodes when you move past prototyping, and see the Base network page for the current supported transports.
A quick rule: if a transaction is supposed to cost real money or touch a user's funds, you are on mainnet (8453). If you are iterating on contract logic, you are almost certainly on Sepolia (84532).
Base chain settings for wallets and frameworks
Wallets and most frameworks need the same four values. Add Base manually in MetaMask, Rabby, or any EVM wallet with:
- Network name: Base
- RPC URL:
https://base.api.onfinality.io/public - Chain ID:
8453 - Currency symbol: ETH
- Block explorer:
https://basescan.org
For Base Sepolia, change the name to Base Sepolia, the RPC URL to https://base-sepolia.api.onfinality.io/public, the chain ID to 84532, and the explorer to https://sepolia.basescan.org.
If you are wiring this into a frontend, define the chain once and reuse it. A viem chain definition looks like this:
import { defineChain } from "viem";
export const base = defineChain({
id: 8453,
name: "Base",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["https://base.api.onfinality.io/public"] },
},
blockExplorers: {
default: { name: "BaseScan", url: "https://basescan.org" },
},
});
Keep the chain ID and RPC URL in one config object rather than scattering strings across components. When you later switch to a private or dedicated endpoint, you change one line instead of hunting through the codebase.
A minimal curl example you can run now
The fastest sanity check is a single JSON-RPC call over HTTP. This asks Base for the latest block number:
curl -s https://base.api.onfinality.io/public \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
The response is a hex string, not a decimal number:
{ "jsonrpc": "2.0", "id": 1, "result": "0x13a2f1c" }
Convert it with parseInt(result, 16). If the number is climbing between calls, your endpoint is live and synced. If it returns an error object, jump to the debugging section below.
Two more calls worth keeping in your notes. eth_chainId confirms you are talking to the network you think you are:
curl -s https://base.api.onfinality.io/public \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
It should return 0x2105, which is 8453 in hex. And eth_getBalance checks an address:
curl -s https://base.api.onfinality.io/public \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x0000000000000000000000000000000000000000","latest"],"id":1}'
Replace the zero address with a real one. The result is in wei, so divide by 1e18 for ETH.
Reading the response without guessing
A JSON-RPC response is always one of two shapes. Either it has a result field, or it has an error object with a code and message. There is no third case, and there is no HTTP status code that tells you the whole story, because many RPC errors still return HTTP 200.
That is why "it returned 200 but my app crashed" is so common. Your client must check for the error field, not just the HTTP status. Most mature libraries such as viem and ethers do this for you, but hand-rolled fetch calls often do not.
When you get an error, read the code first:
-32601means the method does not exist or is not enabled on that endpoint.-32602means your parameters are malformed, often a missing0xprefix or a wrong argument count.-32000is a generic server-side error and often points to a request the node could not serve, such as a range that is too large.- A
429or a message about rate limits means you are sending too many requests for the endpoint tier you are on.
Debug path: from symptom to fix
When something breaks, work down this table instead of changing code at random.
| Symptom | Likely cause | First thing to check |
|---|---|---|
| Balance shows 0 on mainnet | Wrong network | eth_chainId returns 8453? |
eth_chainId returns 84532 | You are on Sepolia | Switch the RPC URL to mainnet |
-32601 on a trace call | Method not enabled on shared endpoint | Request trace/archive access or a dedicated node |
429 under load | Public endpoint rate limits | Move to a private or dedicated endpoint |
eth_getLogs times out | Block range too wide | Narrow the fromBlock/toBlock window |
| Transaction stuck pending | Gas price too low for current conditions | Re-check gas and resubmit |
| Works in curl, fails in browser | CORS or mixed content | Confirm the URL is HTTPS and the endpoint allows your origin |
The eth_getLogs case deserves a note. Public endpoints often cap the block range you can query in one call. If you are indexing events, page through history in smaller windows rather than asking for millions of blocks at once. This is a request-shape problem, not a network problem, and it is one of the most frequent reasons a Base indexer appears to "stop working."
When the public example is not enough
A public endpoint is perfect for the example above and for light development. It is not designed for production traffic with bursty load, WebSocket subscriptions, or heavy archive queries. Signs you have outgrown it:
- You see intermittent
429responses during peak usage. - You need
eth_subscribeover WebSocket for real-time events. - You need historical state or trace data that shared endpoints do not expose.
- You want predictable capacity instead of best-effort sharing.
At that point the decision is between a managed RPC API and a dedicated node. OnFinality offers both: a managed RPC API service for teams that want an endpoint without running infrastructure, and dedicated nodes for workloads that need isolated capacity. If you are weighing providers generally, the provider selection guide walks through the criteria. For Base specifically, start from the Base network page and the Base Sepolia page, then check pricing and the full list of supported networks.
Testing on Base Sepolia without wasting time
Sepolia is where most of your example-running will happen, so set it up properly once. The chain ID is 84532, the explorer is sepolia.basescan.org, and the endpoint is https://base-sepolia.api.onfinality.io/public. You will need test ETH to deploy contracts or send transactions; get it from a Base Sepolia faucet, then confirm it arrived with eth_getBalance before you debug anything else.
A common trap: a contract deployed on Sepolia has a different address than the same contract on mainnet. Never copy an address between the two. Keep separate environment variables for each network and load them by chain ID so a testnet address can never leak into a mainnet transaction.
Key Takeaways
- Base mainnet uses chain ID 8453; Base Sepolia uses 84532. Confirm with
eth_chainIdbefore debugging anything else. - The public OnFinality endpoints are
https://base.api.onfinality.io/publicandhttps://base-sepolia.api.onfinality.io/public. - JSON-RPC errors can arrive with an HTTP 200 status, so always check the response body for an
errorfield. eth_getLogsblock-range limits and429responses are the two most common reasons a working example breaks under load.- Move to a private or dedicated endpoint when you need WebSocket subscriptions, archive or trace data, or predictable capacity.
Frequently Asked Questions
What is the Base RPC URL?
For mainnet, the public OnFinality endpoint is https://base.api.onfinality.io/public. For testnet, use https://base-sepolia.api.onfinality.io/public. Production apps typically use a private or dedicated endpoint instead.
What is Base's chain ID?
Base mainnet is 8453 (hex 0x2105). Base Sepolia is 84532.
Why does my Base RPC call return an error with HTTP 200?
JSON-RPC reports application-level errors inside the response body, not through the HTTP status. Check the error object and its code field.
Can I use WebSocket with Base? WebSocket support depends on the endpoint tier. Public HTTP endpoints are for basic calls; check the Base network page for supported transports and consider a dedicated node for subscriptions.
Do I need an archive node for Base? Only if you query historical state or wide log ranges. Standard recent-block queries work on a normal endpoint. Archive and trace access is usually a dedicated-node feature.
How do I avoid rate limits on Base RPC? Batch and cache where possible, avoid tight polling loops, and move to a private or dedicated endpoint when your request volume grows. See RPC pricing for tiers.