By Eco research. Updated April 2026.
JSON-RPC is a stateless, lightweight remote procedure call protocol that encodes requests and responses as JSON objects, and it is the standard API protocol for every Ethereum node, with every major wallet library (ethers.js, viem, web3.js) and browser extension (MetaMask, Rainbow, Coinbase Wallet) communicating with Ethereum over JSON-RPC. First defined in the JSON-RPC 2.0 specification published in 2013, the protocol predates Ethereum itself. Ethereum's adoption of JSON-RPC was specified in the Ethereum Execution API specification, which defines the full set of eth_* methods and their request/response shapes.
What Is JSON?
JSON (JavaScript Object Notation) is a text-based data format derived from JavaScript object syntax that uses key-value pairs, arrays, strings, numbers, booleans, and null as its primitive types. A JSON object is a comma-separated list of key-value pairs enclosed in curly braces; a JSON array is an ordered list enclosed in square brackets. JSON has no native support for binary data, which is why Ethereum values like addresses and bytecode are encoded as hexadecimal strings prefixed with "0x" in JSON-RPC payloads.
JSON was formally specified in IETF RFC 7159 (2014, superseded by RFC 8259 in 2017). Its dominance over XML and SOAP in API design traces to simplicity: a JSON object can be parsed natively in every mainstream programming language without an external library, and its text encoding is human-readable for debugging. The trade-off is size: JSON is verbose compared to binary protocols like Protocol Buffers (protobuf) or MessagePack. Ethereum uses JSON-RPC over HTTP and WebSocket for its public API layer, while peer-to-peer node communication uses a binary protocol called RLPx (devp2p).
How Does the JSON-RPC Protocol Work?
JSON-RPC 2.0 defines a request object with four fields: "jsonrpc" (always the string "2.0"), "method" (a string naming the procedure to call), "params" (an array or object of parameters), and "id" (a client-chosen identifier used to match responses to requests). A response object contains "jsonrpc", "id", and either a "result" field (on success) or an "error" field (on failure). Notifications, which omit the "id", are one-way messages that expect no response and are used for WebSocket subscription events.
A minimal eth_getBalance request looks like this in JSON:
{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","latest"],"id":1}
The server responds with the balance in wei encoded as a hexadecimal string: {"jsonrpc":"2.0","id":1,"result":"0x1bc16d674ec80000"}. Converting 0x1bc16d674ec80000 from hex to decimal gives 2,000,000,000,000,000,000 wei, or 2 ETH. This hex encoding for numeric values is an Ethereum-specific convention on top of the base JSON-RPC protocol. The JSON-RPC specification itself does not mandate hex; Ethereum chose it to handle 256-bit integers that would overflow JSON's numeric type.
Error responses carry a numeric code and message. Code -32602 means invalid parameters, -32601 means the method was not found, and -32000 to -32099 are reserved for server-defined errors (Ethereum nodes use this range for execution errors like insufficient gas or reverted transactions). Clients should always check for the "error" key in responses rather than assuming a missing key means success.
What Are the Core Ethereum JSON-RPC Methods?
Ethereum's execution API defines over 40 eth_* methods, covering state reads, transaction submission, block and receipt queries, and filter management. The table below covers the methods used most heavily by wallets, DeFi front-ends, and infrastructure tools.
Method | Category | What it does | Key parameter |
eth_call | State read | Executes a message call against the current or historical state without creating a transaction | Transaction object + block tag |
eth_sendRawTransaction | Write | Submits a signed, RLP-encoded transaction to the mempool | Signed transaction hex |
eth_getBalance | State read | Returns the ETH balance of an address at a specified block | Address + block tag |
eth_getTransactionReceipt | Query | Returns the receipt for a confirmed transaction, including logs | Transaction hash |
eth_getLogs | Query | Returns log entries matching a filter (address, topics, block range) | Filter object |
eth_blockNumber | State read | Returns the number of the most recent block | None |
eth_getCode | State read | Returns the bytecode deployed at an address | Address + block tag |
eth_estimateGas | Simulation | Estimates the gas required to execute a transaction | Transaction object |
eth_call is the workhorse for DeFi applications. When a front-end needs to display a token balance, quote a swap, or check whether a user's position is undercollateralized, it issues an eth_call. The node executes the contract bytecode against its current state and returns the result without writing anything to the chain. Alchemy's published data indicates that eth_call makes up approximately 40% of all RPC requests on their network, making it the most-called method by volume. eth_getLogs is the second-most-called method, driven by indexers like The Graph that reconstruct onchain state from event logs.
How Does Batch JSON-RPC Work?
Batch JSON-RPC allows a client to send multiple request objects in a single HTTP call by wrapping them in a JSON array. The server processes all requests and returns an array of response objects in the same call. Batch requests dramatically reduce latency for applications that need to fetch multiple independent pieces of state simultaneously, such as an indexer reading the balances of 500 wallets or a DeFi dashboard querying 20 token reserves in one round trip.
A batch request is structurally simple: instead of sending a single JSON object, the client sends [{request1}, {request2}, ..., {requestN}]. The server may process requests in any order and return responses in any order; clients match responses to requests using the "id" field. The JSON-RPC 2.0 specification explicitly permits batch processing, and all major Ethereum RPC providers support it. Alchemy, Infura, and QuickNode each impose batch size limits (typically 100 to 1,000 requests per batch) to prevent resource exhaustion.
ethers.js v6 and viem both support batching through provider configuration. In viem, setting batch: { multicall: true } on the public client automatically coalesces multiple eth_call invocations (such as multiple useReadContract hooks in a React component) into a single Multicall3 contract call, which is more efficient than JSON-RPC batching for read-only calls because it uses a single network round trip and a single node execution context. For non-eth_call methods (like fetching transaction receipts for a list of hashes), direct JSON-RPC batching remains the appropriate tool.
What Is the Difference Between WebSocket and HTTP JSON-RPC?
HTTP JSON-RPC uses request/response semantics: the client sends one request and receives one response, then the connection may close or be reused via HTTP keep-alive. WebSocket JSON-RPC opens a persistent bidirectional connection that remains open until explicitly closed, allowing the server to push messages to the client without the client polling. These are two different transport models suited to different use cases.
Subscriptions are the primary reason to use WebSocket RPC. The eth_subscribe method (specific to WebSocket connections) creates a server-push subscription for events like newHeads (new block headers), logs (matching a filter), and pendingTransactions (new transactions entering the mempool). Each new event triggers a JSON-RPC notification from the server with a subscription ID, which the client can filter by. Ethers.js wraps this in a provider.on("block", ...) event handler; under the hood, it calls eth_subscribe over WSS and routes incoming notifications to the registered callback.
HTTP polling is simpler operationally (stateless, cacheable, works with any load balancer) but introduces latency equal to the polling interval. For applications that display real-time price feeds, pending transaction status, or live order book depth from onchain state, WebSocket subscriptions cut update latency from seconds to under 100 milliseconds on average. Alchemy's WebSocket documentation notes that subscription events typically arrive within one to two block propagation delays (~500ms on mainnet).
How Does ABI Encoding Relate to JSON-RPC?
The JSON-RPC layer handles transport and routing, but the payload of an eth_call or eth_sendRawTransaction contains data encoded according to the Ethereum ABI (Application Binary Interface), not JSON. ABI encoding is a compact binary format that encodes function selectors (4-byte keccak256 hash of the function signature) and argument values into the data field of a transaction or call object. Wallet libraries like ethers.js and viem perform ABI encoding automatically when a developer calls a contract function using an ABI definition.
The separation matters because JSON-RPC is the outer envelope (it routes the call to the right node and method) while ABI encoding is the inner payload (it tells the EVM which contract function to execute and with what arguments). A developer building directly on top of JSON-RPC (for example, writing a low-level tx assembler or a custom MEV searcher) must handle both layers: JSON-RPC formatting for the transport and ABI encoding for the call data. Libraries abstract both, but understanding the stack is essential for debugging "invalid params" errors or unexpected revert data in eth_call responses.
The Solidity ABI specification documents the encoding rules in full. Each type has a defined encoding: uint256 is padded to 32 bytes big-endian, dynamic types like bytes and string are encoded as an offset pointer followed by a length and the actual data. Incorrect ABI encoding is a common source of bugs in custom script tooling and is also the mechanism by which ABI-level encoding attacks (like function selector clashing) work.
Why Does JSON-RPC Matter for Blockchain Developers?
JSON-RPC is the universal interface layer of the Ethereum ecosystem. Every execution client (Geth, Nethermind, Besu, Erigon, Reth) exposes the same JSON-RPC API, meaning any tool built to that interface works with any client. The Ethereum Execution API specification on GitHub defines this contract and is versioned separately from individual client implementations. Deviations (like extra methods added by a specific client for debugging) are documented as extensions and do not break standard tooling.
Cross-chain infrastructure depends on consistent JSON-RPC interfaces. Eco's routing and settlement layer queries state across 15 supported chains by calling the same eth_* methods on each chain's RPC endpoints. EVM-compatible chains like Polygon, Arbitrum, Base, BNB Chain, and Unichain all implement the Ethereum Execution API, meaning a single code path in the router can query token balances, simulate swap routes, and submit transactions across every EVM chain using identical JSON-RPC calls. Non-EVM chains like Solana use different RPC interfaces, requiring chain-specific adapters.
Performance optimization for JSON-RPC-heavy applications centers on three levers: batching (fewer round trips), multicall (fewer EVM executions per round trip), and caching (storing the results of deterministic calls against historical blocks, which never change). Archive node access is required for eth_call against historical blocks older than the default 128-block window that most full nodes retain in memory. RPC providers including Alchemy and Infura offer archive node access for teams that need historical state queries.
FAQ
What is the difference between eth_call and eth_sendRawTransaction?
eth_call simulates a contract call against the current state without creating a transaction or consuming gas on the actual chain. It is used for read operations and for pre-flight simulation. eth_sendRawTransaction submits a fully signed, gas-paying transaction to the mempool for inclusion in a block. Any state change requires eth_sendRawTransaction; eth_call results are never written to the blockchain.
Why do Ethereum JSON-RPC numbers use hex strings instead of integers?
Ethereum operates with 256-bit integers (uint256), which are too large for JSON's number type, which can only safely represent 53-bit integers. All numeric values in Ethereum's JSON-RPC are hex strings (prefixed with "0x") to handle the full 256-bit range without overflow. Wallet libraries decode these hex strings into native BigInt types automatically.
Can I run JSON-RPC calls against a local Ethereum node?
Yes. Any Ethereum execution client (Geth, Nethermind, Besu, Erigon, or Reth) exposes a JSON-RPC server on localhost:8545 by default when started. Tools like Hardhat and Anvil also run local JSON-RPC servers for development and testing. Local nodes give unlimited request rates and complete control but require syncing the chain or operating in fork mode against a remote RPC endpoint.
Do all EVM-compatible chains use the same JSON-RPC methods?
EVM-compatible chains implement the core eth_* methods defined in the Ethereum Execution API. Most chains add chain-specific extensions (for example, Arbitrum adds arb_* methods and Base adds debug_* methods in its Bedrock stack), but the base eth_* surface is standard. Non-EVM chains like Solana use completely different RPC interfaces and require chain-specific tooling.
What is eth_subscribe and why does it need WebSocket?
eth_subscribe creates a server-push subscription that delivers events (new blocks, matching logs, pending transactions) to the client as they occur, without polling. Because it requires the server to initiate messages, it only works over a persistent WebSocket connection. HTTP JSON-RPC's request/response model cannot support server-initiated pushes. Ethers.js and viem handle the WebSocket connection management and subscription routing automatically when the provider URL starts with "wss://".
Related reading
Sources and methodology. Ethereum Execution API method definitions verified against the ethereum/execution-apis repository on GitHub. JSON-RPC 2.0 specification from jsonrpc.org. JSON RFC from IETF RFC 8259. Ethereum ABI specification from Solidity documentation. RPC usage patterns sourced from Alchemy developer documentation. Figures reflect protocol specifications as of April 2026.
