JSON-RPC

On this page 7

JSON-RPC is a stateless, lightweight remote procedure call protocol that encodes a call to a named method, its parameters and its result or error as JSON objects. Among APIs, it represents the procedure-oriented style: a client asks a server to run a specific function, rather than manipulating resources as a REST API does.

The current version, JSON-RPC 2.0, is a short specification that defines only the message format and leaves the transport open. In practice it runs over HTTP, WebSocket, TCP sockets and standard input and output, which explains its wide use in blockchain node interfaces, editor tooling such as the Language Server Protocol and the Model Context Protocol for AI tools.

How JSON-RPC messages work

A JSON-RPC exchange consists of a request object sent by the client and, in most cases, a response object returned by the server. Every message carries the member "jsonrpc": "2.0" so that both sides know which version of the protocol applies.

A request contains the method name, optional params given either as an array (by position) or as an object (by name), and an id that can be a string or a number. The server answers with the same id plus either a result member or an error member, never both. The id is what lets a client match responses to requests when several calls are in flight over one connection.

MemberAppears inPurpose
jsonrpcRequest and responseMust be exactly the string 2.0
methodRequestName of the procedure to invoke
paramsRequest (optional)Arguments as an array or an object
idRequest and responseCorrelates a response with its request
resultSuccessful responseReturn value of the method
errorFailed responseObject with code, message and optional data
Members of JSON-RPC 2.0 messages

A request without an id is a notification. The server must not reply to it, which makes notifications suitable for fire-and-forget events such as progress updates or log messages, but it also means the client never learns whether the call failed.

Batches, notifications and error codes

JSON-RPC 2.0 allows a client to send an array of requests as a batch. The server may process them in any order and in parallel, and it returns an array of responses that the client reassembles by id. Notifications inside a batch produce no entries, and a batch consisting only of notifications produces no response at all.

Errors use integer codes with fixed meanings for the protocol layer. The range from -32768 to -32000 is reserved for the specification and implementations, while applications should define their own codes outside that block.

CodeNameMeaning
-32700Parse errorThe server received invalid JSON
-32600Invalid requestThe JSON is not a valid request object
-32601Method not foundThe method does not exist or is unavailable
-32602Invalid paramsThe parameters are wrong for the method
-32603Internal errorA server-side failure occurred
-32000 to -32099Server errorReserved for implementation-defined errors
Standard JSON-RPC 2.0 error codes

JSON-RPC compared with other API styles

JSON-RPC sits at the simple end of the procedure-call spectrum. It shares the call-a-function model of gRPC but uses text instead of a binary schema, and it differs from a REST API by putting the operation in the message body rather than in the URL and HTTP method.

AspectJSON-RPCREST APIgRPCGraphQL
Core modelMethod callsResources and HTTP verbsMethod callsTyped query language
EncodingJSON textUsually JSONProtocol Buffers binaryJSON
TransportAnyHTTPHTTP/2Usually HTTP
SchemaNone built inOptional, often OpenAPIMandatory .proto filesMandatory schema
HTTP cachingNot usable by defaultNative with GETNot usableLimited
Bidirectional callsYes over socketsNoYes with streamingVia subscriptions
JSON-RPC and neighboring API styles

Because JSON-RPC typically sends every call as an HTTP POST to a single API endpoint, it gives up the caching, status-code semantics and resource-oriented URLs that make REST friendly to proxies and browsers. In return it offers a uniform envelope, transport independence and symmetric communication in which either peer can call the other.

When to choose JSON-RPC

JSON-RPC fits best where the interface is naturally a set of actions rather than a set of resources, and where both sides want a minimal, language-neutral contract.

SituationFitReason
Tool and editor protocols over stdioStrongBidirectional calls with no HTTP needed
Blockchain node interfacesStrongMany distinct commands with small payloads
Internal command APIsGoodSimple to implement in any language
Public web API with heavy readsWeakLoses HTTP caching and resource semantics
High-throughput service meshWeakText encoding is slower than binary gRPC
Decision guide for JSON-RPC

Teams that adopt JSON-RPC usually add what the protocol omits: a machine-readable method catalog, authentication at the transport layer, request size limits and versioning rules. Without these, the flexibility of free-form params quickly turns into undocumented and fragile contracts.

Limitations and common mistakes

  • Returning HTTP errors instead of error objects: clients expect a JSON-RPC error with a code; mixing HTTP 500 responses for application failures breaks generic client libraries.
  • Reusing ids for concurrent requests: duplicate ids make responses ambiguous on a shared connection.
  • Using notifications for important actions: the caller receives no confirmation and no error.
  • Ignoring batch limits: unbounded batches let one client consume large amounts of server time.
  • Mixing 1.0 and 2.0 semantics: the older version used null fields and lacked named params, so the version member must be checked.

Security is outside the specification. Authentication, authorization per method and rate limiting must be added by the transport or the application, and exposing a node or tool interface with powerful methods on an open port is a frequent and serious misconfiguration.

The idea in brief

JSON-RPC is a simple, agreed way for one program to ask another program to do something. The request is a short note written in JSON that says which task to run, which values to use and a reference number. The answer comes back with the same number and either the result or an explanation of what went wrong.

It works much like ordering by a numbered ticket at a counter. You hand over a ticket that says what you want, and when your number is called, you get either your order or a reason it is unavailable. The ticket format stays the same whether the order goes by phone, by messenger or across the counter.

A practical scenario

A team builds a code editor plugin that offers autocompletion, error highlighting and renaming for an internal programming language. The editor and the language engine run as two separate processes connected through standard input and output, with no web server involved.

They use JSON-RPC 2.0 for every interaction. When the user types, the editor sends a request with the method name for completion, the file path and the cursor position, plus an id such as 57. The engine replies with id 57 and a list of suggestions, typically within tens of milliseconds. When the engine finishes analyzing a file, it sends a notification with diagnostics, and the editor draws the red underlines without replying.

Because the message format is fixed, the same engine later works with a second editor after only a small adapter is written. The outcome is one language engine serving several editors, with errors reported through standard codes such as -32602 when the editor passes an invalid position.