Ethereum RPC Guide: How to Connect and Build on Ethereum (2026)
Ethereum is the reference Layer 1 for smart contract development. Every wallet, dApp, and on-chain protocol relies on an RPC endpoint to communicate with the network. This guide walks through everything you need to connect to Ethereum, use its JSON-RPC methods in production, understand its finality model, and avoid the issues that catch developers off-guard.
What is an Ethereum RPC Endpoint
An Ethereum RPC endpoint is a URL that lets your application interact with the Ethereum blockchain without running a node. You send JSON-RPC 2.0 requests over HTTP or WebSocket, and the node responds with block data, balances, transaction receipts, contract state, or confirmation that your transaction was accepted.
Two connection types are available:
HTTP (HTTPS): Stateless request-response. Use this for reading data (balances, logs, receipts) and broadcasting signed transactions. Simpler to use, sufficient for most use cases.
WebSocket (WSS): Persistent connection. Use this when you need real-time event subscriptions via eth_subscribe: new block headers, contract event logs, pending transactions. The node pushes data to your app as it arrives.
Default rule: use HTTPS, switch to WebSocket when you need real-time subscriptions.
Public Ethereum RPC Endpoints
The following public endpoints are suitable for testing only. Do not use them in production.
| Network | RPC URL | Chain ID |
|---|---|---|
| Ethereum Mainnet | https://eth.llamarpc.com | 1 |
| Ethereum Mainnet | https://cloudflare-eth.com | 1 |
| Ethereum Mainnet | https://ethereum.publicnode.com | 1 |
| Ethereum Hoodi Testnet | https://rpc.hoodi.ethpandaops.io | 560048 |
Public endpoints are rate-limited to a few requests per second, run on shared infrastructure, and provide no support or SLA. For any app with real users or real transaction volume, use a dedicated authenticated endpoint.
BoltRPC provides dedicated Ethereum RPC at https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY. Start with a free 2-week trial at trial.boltrpc.io.
Ethereum RPC Methods
Ethereum’s full JSON-RPC 2.0 spec is implemented by all major execution clients. Here are the five methods you will use most in production:
eth_getBalance
Returns the ETH balance of an address at a given block tag.
curl -X POST \
"https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xYourAddress", "latest"],
"id": 1
}'
Result is in wei (hex). Divide by 10^18 to get ETH. Use block tags "latest", "safe", "finalized" as needed (see the finality section below).
eth_call
Executes a read-only call against a contract without creating a transaction. Use this for view and pure functions.
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(
"https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY"
);
const abi = ["function totalSupply() view returns (uint256)"];
const contract = new ethers.Contract("0xTokenAddress", abi, provider);
const supply = await contract.totalSupply();
console.log("Total supply:", supply.toString());
eth_sendRawTransaction
Broadcasts a signed transaction to the network. Your library signs the transaction locally and passes the raw hex to this method.
const wallet = new ethers.Wallet("0xYourPrivateKey", provider);
const tx = await wallet.sendTransaction({
to: "0xRecipientAddress",
value: ethers.parseEther("0.01"),
maxFeePerGas: ethers.parseUnits("20", "gwei"),
maxPriorityFeePerGas: ethers.parseUnits("1.5", "gwei")
});
const receipt = await tx.wait();
console.log("Confirmed in block:", receipt.blockNumber);
eth_getLogs
Fetches event logs matching a filter. This is the foundation of event-driven Ethereum apps: indexing transfers, tracking oracle updates, monitoring DEX swaps.
const logs = await provider.getLogs({
address: "0xContractAddress",
topics: [ethers.id("Transfer(address,address,uint256)")],
fromBlock: 21000000,
toBlock: 21001000
});
console.log(`Found ${logs.length} Transfer events`);
Keep block ranges under 2,000 blocks per request unless your provider explicitly supports larger ranges. See the common issues section for pagination.
eth_subscribe
WebSocket-only. Pushes events to your app in real time. Supports newHeads, logs, newPendingTransactions.
const wsProvider = new ethers.WebSocketProvider(
"wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY"
);
wsProvider.on("block", (blockNumber) => {
console.log("New block:", blockNumber);
});
const filter = {
address: "0xContractAddress",
topics: [ethers.id("Transfer(address,address,uint256)")]
};
wsProvider.on(filter, (log) => {
console.log("Transfer:", log.transactionHash);
});
Connecting Step by Step
ethers.js
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(
"https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY"
);
const blockNumber = await provider.getBlockNumber();
console.log("Connected. Block:", blockNumber);
Web3.py
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(
"https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY"
))
if w3.is_connected():
print(f"Connected. Block: {w3.eth.block_number}")
balance_wei = w3.eth.get_balance("0xYourAddress")
balance_eth = w3.from_wei(balance_wei, "ether")
print(f"Balance: {balance_eth} ETH")
For WebSocket in Web3.py:
from web3 import Web3
from web3.middleware import ExtraDataToPOAMiddleware
w3 = Web3(Web3.WebsocketProvider(
"wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY"
))
curl
curl -X POST \
"https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x13d99f0"
}
Convert the hex result with parseInt("0x13d99f0", 16).
For MetaMask setup and manual network configuration, see the Ethereum RPC endpoint page.
Ethereum-Specific Considerations
EIP-1559 Fee Model
Ethereum uses a two-part fee structure since the London upgrade. Every transaction specifies:
maxFeePerGas: the maximum total you will pay per gas unit (base fee + priority fee)maxPriorityFeePerGas: the tip paid directly to the validator
The base fee is set by the protocol and burned. It adjusts up or down based on block utilization. You can read the current base fee from eth_feeHistory or use your library’s getFeeData() method:
const feeData = await provider.getFeeData();
console.log("Base fee:", ethers.formatUnits(feeData.gasPrice, "gwei"), "gwei");
console.log("Max fee suggestion:", ethers.formatUnits(feeData.maxFeePerGas, "gwei"), "gwei");
Never hardcode gas prices. Always fetch live fee data before submitting a transaction to avoid overpaying during spikes or getting stuck during surges.
PoS Finality and Block Tags
Ethereum uses Proof of Stake. Blocks progress through three states:
| Tag | Meaning | When to Use |
|---|---|---|
latest | Most recent block (may reorg) | Balance reads, general queries |
safe | Unlikely to reorg (a few epochs behind head) | Transaction status where near-finality is sufficient |
finalized | Cryptographically final (two epochs, ~12 min) | Bridges, cross-chain transfers, custody systems |
Most applications use latest. If you are building infrastructure that depends on irreversibility (a bridge releasing funds, a custody system crediting deposits), use finalized. Using latest for bridge finality has historically led to losses from reorg-based exploits.
const finalizedBlock = await provider.getBlock("finalized");
console.log("Finalized block:", finalizedBlock.number);
Archive vs Full Node
A full node stores recent state (typically the last 128 blocks plus full block history). An archive node stores all historical state from genesis.
You need an archive node when:
- Querying balances or contract state at old block numbers
- Backfilling historical
eth_getLogsover large ranges - Running historical simulations or analytics
For most dApps and trading bots that only interact with current state, a full node is sufficient. BoltRPC provides archive access on request.
Common Issues and Fixes
eth_getLogs block range exceeded
Most providers cap eth_getLogs ranges to avoid overloading the node. If you receive an error like block range too large, paginate your requests:
async function getLogsInRange(provider, filter, fromBlock, toBlock, chunkSize = 2000) {
const logs = [];
for (let start = fromBlock; start <= toBlock; start += chunkSize) {
const end = Math.min(start + chunkSize - 1, toBlock);
const chunk = await provider.getLogs({ ...filter, fromBlock: start, toBlock: end });
logs.push(...chunk);
}
return logs;
}
Nonce errors on eth_sendRawTransaction
If you get nonce too low or replacement transaction underpriced, your local nonce is out of sync with the network. Fetch the current nonce before every transaction in non-sequential flows:
const nonce = await provider.getTransactionCount("0xYourAddress", "pending");
Use "pending" if you have unconfirmed transactions in flight.
WebSocket connection drops
WebSocket connections close for many reasons: provider restarts, network hiccups, idle timeouts. Always implement reconnection:
function createWsProvider() {
const ws = new ethers.WebSocketProvider(
"wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY"
);
ws.websocket.on("close", () => {
console.log("WebSocket closed. Reconnecting...");
setTimeout(() => createWsProvider(), 3000);
});
return ws;
}
Stale results after block reorg
Ethereum PoS has very rare but possible short reorgs (1-2 blocks). If you cache block data by number, invalidate on reorg:
wsProvider.on("block", async (blockNumber) => {
const block = await provider.getBlock(blockNumber);
if (lastHash && block.parentHash !== lastHash) {
console.warn("Reorg detected at", blockNumber);
}
lastHash = block.hash;
});
Choosing a Provider
For Ethereum specifically, consider these factors:
Finality support. Confirm the provider exposes safe and finalized block tags. Some older or public endpoints only support latest.
Archive access. If your app queries historical state, verify whether archive is included or costs extra.
Pricing model. Some providers use compute unit (CU) pricing, where heavy methods like eth_getLogs or debug_traceTransaction multiply your costs unpredictably. Flat-rate pricing removes this variable.
Infrastructure certification. For enterprise teams with procurement or compliance requirements, ISO 27001:2022 certification is meaningful. BoltRPC runs on Matrixed.Link’s ISO/IEC 27001:2022 certified infrastructure.
BoltRPC offers dedicated Ethereum RPC with flat-rate pricing from $49/mo, WebSocket support, EU infrastructure, and professional support on all plans. Trusted by Chainlink, Tiingo, Gains Network, Enjin.
Start your free 2-week trial at trial.boltrpc.io.
For endpoint details, MetaMask setup, and full network specs, see the Ethereum RPC endpoint page.
FAQ
What is the Ethereum RPC endpoint URL?
BoltRPC’s Ethereum Mainnet endpoint is https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY. Replace YOUR_KEY with your key from trial.boltrpc.io. The WebSocket endpoint is wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY. Chain ID is 1.
What is the difference between latest, safe, and finalized block tags on Ethereum?
latest is the most recent block and may be reorganized. safe is a recent block unlikely to reorg. finalized is cryptographically final after two epochs (approximately 12 minutes). Use latest for general reads, safe for transaction status checks, finalized for bridge and custody systems where irreversibility is required.
Do I need an archive node for Ethereum?
Only if you query historical state (balances at old blocks, large eth_getLogs ranges, historical simulations). Standard full nodes keep only recent state. For most dApps and bots working with current state, a full node is sufficient and cheaper.
What is EIP-1559 and how does it affect gas fees?
EIP-1559 replaced the legacy single-price gas auction with a base fee (burned, set by the protocol) plus a priority fee (tip to the validator). Your transaction sets maxFeePerGas as a ceiling and maxPriorityFeePerGas as the tip. Always fetch live fee data with provider.getFeeData() rather than hardcoding gas prices, since the base fee adjusts every block.