Guide To JSON RPC 2.0
Introduction
Modern distributed systems constantly need a way for one application to communicate with another application across a network. A frontend may need to ask a backend server for data, a blockchain client may need to interact with a node, or an AI application may need to invoke tools exposed by another service. While there are many communication styles available today, Remote Procedure Call, commonly called RPC, remains one of the simplest and most intuitive approaches.
JSON-RPC is one such RPC protocol, designed around a very clear philosophy: communication should be lightweight, predictable, and easy to implement. The protocol avoids unnecessary complexity and focuses only on defining how requests and responses should look. It deliberately stays transport agnostic, meaning it does not force developers to use HTTP, WebSockets, TCP, or any other specific communication layer.
In this tutorial, we will explore JSON-RPC 2.0 in depth, understand its core components, examine how requests and responses work, study error handling, notifications, batch processing, and finally understand where JSON-RPC fits in modern software systems.
Understanding the Core Idea Behind JSON-RPC
Before discussing syntax and message structures, it is important to understand the fundamental problem JSON-RPC solves. Imagine that one application wants another application to execute a function remotely.
For example:
- A frontend application wants a backend to calculate totals
- A wallet application wants a blockchain node to fetch account balances
- An AI host wants an external MCP server to execute a tool
- A monitoring dashboard wants to query metrics from another service
Instead of exchanging arbitrary messages, JSON-RPC standardizes communication into a very simple pattern:
- A client sends a request
- The server executes a method
- The server sends back a response
Conceptually, it feels almost like calling a local function, except the function exists on another machine or service. This is why the term “Remote Procedure Call” exists.
What Makes JSON-RPC Lightweight
One of the major design goals of JSON-RPC is simplicity. The protocol defines only a few core concepts:
- Request objects
- Response objects
- Notifications
- Error objects
- Batch requests
That is essentially the entire protocol. Unlike larger frameworks that include routing, resource modeling, version negotiation, or transport-level rules, JSON-RPC focuses only on message structure.
This minimalism is precisely why JSON-RPC is popular in areas such as:
- Blockchain systems
- Internal microservice communication
- Developer tooling
- AI protocols like MCP
- Lightweight backend services
JSON-RPC Is Transport Agnostic
One extremely important characteristic of JSON-RPC is that it does not care how messages travel between systems.
The protocol can work over:
- HTTP
- HTTPS
- TCP sockets
- WebSockets
- IPC channels
- Message queues
- STDIO streams
The only requirement is that the messages themselves follow JSON-RPC formatting rules. This flexibility makes JSON-RPC highly reusable across different environments.
For example:
| Environment | Possible Transport |
|---|---|
| Web Application | HTTP |
| Real-time system | WebSocket |
| Local process communication | STDIO |
| Blockchain node | TCP/HTTP |
| AI agent protocol | STDIO/WebSocket |
The Structure of a JSON-RPC Request
The heart of JSON-RPC is the request object. Whenever a client wants the server to execute something, it sends a request.
A standard JSON-RPC 2.0 request looks like this:
{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}
At first glance, this may look like ordinary JSON, but every field has a very specific meaning.
Understanding Each Request Field
The jsonrpc Field
The jsonrpc field specifies the protocol version. In JSON-RPC 2.0, this field must contain:
"jsonrpc": "2.0"
This helps both client and server understand which protocol rules are being used.
The method Field
The method field contains the name of the remote procedure that should be executed. For example:
"method": "subtract"
The server interprets this as “Execute the subtract function remotely”.
Method names are case-sensitive strings. The specification also reserves method names beginning with:
rpc.
for internal protocol usage.
The params Field
The params field contains the arguments passed to the method.
JSON-RPC supports two styles of parameters:
- Positional parameters
- Named parameters
Positional Parameters
In positional parameters, values are passed as an array. Example:
"params": [42, 23]
The server interprets this based on order. So:
subtract(42, 23)
would produce:
19
Named Parameters
Named parameters use objects instead of arrays. Example:
"params": {
"minuend": 42,
"subtrahend": 23
}
This style is often easier to read because each parameter is explicitly named.
The id Field
The id field is extremely important because it connects requests with responses. Example:
"id": 1
When the server replies, it includes the same ID so the client knows which request the response belongs to. This becomes especially useful when multiple requests are happening simultaneously. The ID may be:
- A number
- A string
- Null in certain cases
However, JSON-RPC 2.0 notifications intentionally omit the ID entirely.
Understanding JSON-RPC Responses
When the server successfully executes a method, it returns a response object. Example:
{
"jsonrpc": "2.0",
"result": 19,
"id": 1
}
The response contains three important parts:
- Protocol version
- Result
- Request ID
The result Field
The result field contains the actual output of the method execution. Example:
"result": 19
The important rule here is: "A successful response contains result, not error". The two fields are mutually exclusive.
Error Responses in JSON-RPC
Not every request succeeds. Sometimes:
- The JSON is malformed
- The method does not exist
- Parameters are invalid
- Internal execution fails
In such cases, the server returns an error object instead of a result. Example:
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}
Structure of the Error Object
The error object contains:
| Field | Purpose |
|---|---|
| code | Numeric error identifier |
| message | Human-readable error |
| data | Optional additional details |
The data field is optional but extremely useful for debugging. Example:
"data": "Missing required parameter: subtrahend"
Standard JSON-RPC Error Codes
JSON-RPC defines several built-in error codes.
| Error Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid Request |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
| -32000 to -32099 | Server-defined errors |
Understanding Each Error Type
Parse Error (-32700)
This happens when invalid JSON is received. Example:
{
"jsonrpc": "2.0",
"method":
Since the JSON itself is incomplete, the server cannot even parse the request.
Invalid Request (-32600)
This means the JSON may be syntactically valid, but it does not follow JSON-RPC structure rules. Example:
{
"method": 1
}
The method must be a string, so this request is invalid.
Method Not Found (-32601)
This occurs when the requested procedure does not exist on the server. Example:
"method": "foobar"
If the server has no such method, it returns this error.
Invalid Params (-32602)
This occurs when parameters are missing or incorrectly formatted.
For example, if the subtract method requires two values but receives only one, the server returns an invalid parameter error.
Internal Error (-32603)
This indicates that something failed inside the server during execution.
The request itself may be valid, but the server encountered an unexpected problem.
Notifications in JSON-RPC
One elegant feature of JSON-RPC is notifications. A notification is a request where the client does not expect a response.
Example:
{
"jsonrpc": "2.0",
"method": "update",
"params": [1,2,3]
}
Notice something important:
There is no id field.
Without an ID, the server has no mechanism for correlating a response, so it simply executes the request silently.
When Notifications Are Useful
Notifications are useful for fire-and-forget operations such as:
Logging events Sending telemetry Updating analytics Broadcasting non-critical information
Because no response is expected, notifications reduce overhead and improve efficiency. However, they also introduce risk because the client receives no confirmation that execution succeeded.
Batch Requests in JSON-RPC
JSON-RPC 2.0 also supports batch processing. Instead of sending multiple requests separately, the client can send them together inside an array.
Example:
[
{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
},
{
"jsonrpc": "2.0",
"method": "foobar",
"id": 2
}
]
The server processes each request independently and returns an array of responses.
Why Batch Requests Matter
Batching can significantly improve performance in distributed systems because:
- Fewer network round trips occur
- Multiple operations execute together
- Communication overhead decreases
This becomes especially valuable in high-latency systems or blockchain communication layers.
When JSON-RPC Is a Better Fit
JSON-RPC works especially well when:
- Operations resemble function calls
- High efficiency matters
- Strict request-response control is needed
- The API is internally consumed
- Real-time communication is important
This is why blockchain ecosystems heavily rely on JSON-RPC APIs.
Limitations of JSON-RPC
Despite its elegance, JSON-RPC intentionally leaves several concerns outside the specification. It does not define:
- Authentication
- Authorization
- Service discovery
- API documentation standards
- Version negotiation
- Transport security
These responsibilities must be implemented separately. This simplicity is both a strength and a limitation.