JSON-RPC Migration Guide
JSON-RPC is deprecated. Migrate to either gRPC or GraphQL RPC by July 2026. For a method mapping, decision criteria, and common migration gotchas, see the JSON-RPC Migration Guide.
Refer to the list of RPC or data providers that have enabled gRPC on their full nodes or offer GraphQL RPC. Contact a provider directly to request access. If your RPC or data provider doesn’t yet support these data access methods, ask them to enable support or contact the Sui Foundation team on Discord, Telegram, or Slack for help.
JSON-RPC is deprecated and is planned for deactivation in July 2026. The two replacements are gRPC for full node access and transaction execution, and GraphQL RPC for indexed, filterable reads.
Choose your replacement
You can use one API or both. Most exchanges and indexers end up using both: gRPC for the live data path and transaction submission, GraphQL for ad hoc queries, support tools, and joined reads.
Use gRPC for:
-
Backend services, indexers, and exchange pipelines.
-
Streaming finalized checkpoints with
SubscriptionService.SubscribeCheckpoints. Combined with client-side event filtering, this can serve as a replacement for JSON-RPC WebSocket subscriptions. -
Low-latency point lookups against the current state and recent history of a full node, through
LedgerServiceandStateService. -
Generated, typed clients in TypeScript, Go, Rust, Python, and other languages.
-
Workloads where protocol overhead, message size, and tail latency matter.
Use GraphQL RPC for:
-
Frontends, developer tools, scripts, and dynamic-language clients.
-
Filterable queries over historical transactions, and events.
-
Paginated queries over object and package history.
-
Filtering over live objects by type.
-
Queries that join multiple resources in a single request, such as an address with its owned objects, balances, and recent transactions.
-
Consistent results pinned to the chain state at a specific checkpoint for queries that span multiple different types, or multiple pages.
If you only need to read live state from a single full node, gRPC is usually enough. If you need cross-resource filtering, deep pagination, or want one request to return data assembled from several sources, GraphQL is usually a better fit. For deeper detail on when each API applies, see What is gRPC and GraphQL RPC.
Method mapping
The following table maps common JSON-RPC method families to their gRPC service and GraphQL equivalents. Method names on the gRPC side use the protobuf service in the Sui Full Node gRPC reference. GraphQL equivalents reference the GraphQL field or field path on the schema. Use this table to plan the migration, then consult the linked references for the exact request and response shapes.
| JSON-RPC method | gRPC service and operation | GraphQL field |
|---|---|---|
sui_getCheckpoint, sui_getLatestCheckpointSequenceNumber | LedgerService.GetCheckpoint | Query.checkpoint |
sui_getCheckpoints | No paginated gRPC list. Use LedgerService.GetCheckpoint for point lookups, or SubscriptionService.SubscribeCheckpoints for an ordered stream, depending on your use case. | Query.checkpoints |
sui_getTransactionBlock | LedgerService.GetTransaction | Query.transaction |
sui_multiGetTransactionBlocks | LedgerService.BatchGetTransactions | Query.multiGetTransactions |
sui_getTotalTransactionBlocks | LedgerService.GetCheckpoint on the latest or target checkpoint, then read summary.total_network_transactions | Checkpoint.networkTotalTransactions |
sui_getObject, sui_tryGetPastObject | LedgerService.GetObject (current state and recent history) | Query.object |
sui_multiGetObjects | LedgerService.BatchGetObjects | Query.multiGetObjects |
suix_getBalance, suix_getAllBalances | StateService.GetBalance, StateService.ListBalances | Address.balance, Address.balances |
suix_getCoins, suix_getAllCoins | StateService.ListOwnedObjects with a type filter: 0x2::coin::Coin<T> for one coin type, or the bare 0x2::coin::Coin for all coin types | Address.objects with filter: { type: "0x2::coin::Coin<T>" } for one coin type, or filter: { type: "0x2::coin::Coin" } for all coin types |
suix_getCoinMetadata | StateService.GetCoinInfo | Query.coinMetadata |
suix_getOwnedObjects | StateService.ListOwnedObjects | Address.objects |
suix_getDynamicFields | StateService.ListDynamicFields | Object.dynamicFields |
suix_getDynamicFieldObject | Derive the dynamic field's object ID locally from the parent object ID and the field name, then call LedgerService.GetObject. ListDynamicFields is not needed when you already know the field name. | Object.dynamicObjectField |
suix_queryTransactionBlocks | No direct filtered query. Enumerate finalized transactions through SubscriptionService.SubscribeCheckpoints, or fetch known transactions through LedgerService.GetTransaction. | Query.transactions with a TransactionFilter |
suix_queryEvents | Read events from transaction data returned by LedgerService.GetTransaction or streamed by SubscriptionService.SubscribeCheckpoints | Query.events with an EventFilter |
sui_subscribeEvent, sui_subscribeTransaction (WebSocket) | SubscriptionService.SubscribeCheckpoints, then read events and transactions from each checkpoint | No streaming subscription. Poll Query.events with an EventFilter, or Query.transactions with a TransactionFilter. |
suix_getReferenceGasPrice | LedgerService.GetEpoch (read reference_gas_price from the response) | Epoch.referenceGasPrice |
suix_getStakes, suix_getStakesByIds | No direct gRPC method. Read owned StakedSui objects through StateService.ListOwnedObjects filtered by type, then fetch full contents through LedgerService.GetObject. | Address.objects with filter: { type: "0x3::staking_pool::StakedSui" } |
suix_getValidatorsApy | No direct gRPC method and no APY field. Read validator-set data and compute APY client-side. See issue #23832 for the recommended computation. | No direct APY field. Read validator-set data through Epoch.validatorSet and compute APY client-side. See issue #23832 for the recommended computation. |
sui_executeTransactionBlock | TransactionExecutionService.ExecuteTransaction | Mutation.executeTransaction |
sui_dryRunTransactionBlock, sui_devInspectTransactionBlock | TransactionExecutionService.SimulateTransaction | Query.simulateTransaction |
unsafe_paySui, unsafe_pay, unsafe_payAllSui, unsafe_transferSui, unsafe_transferObject, unsafe_moveCall, unsafe_splitCoin, unsafe_mergeCoins, unsafe_batchTransaction | Build a programmable transaction block with the SDK, then execute it through TransactionExecutionService.ExecuteTransaction | Build a PTB with the SDK, then submit through Mutation.executeTransaction |
sui_getNormalizedMoveModule, sui_getNormalizedMoveFunction, sui_getNormalizedMoveStruct | MovePackageService | Query.package, then MovePackage.module, MoveModule.function, and MoveModule.struct |
The JSON-RPC unsafe_* builder methods do not have a one-to-one replacement on either side. Build transactions with PTBs using the TypeScript SDK or the Rust SDK, then submit the resulting bytes through TransactionExecutionService.ExecuteTransaction.
Migration gotchas
No implicit archival fallback in full node gRPC
JSON-RPC sometimes resolved requests for older data through fallback paths that were transparent to the caller. Full node gRPC does not do this. A full node serves only the data within its retention window, and LedgerService returns "not found" for anything older.
The three interfaces differ in how they reach pruned history:
-
Full node gRPC serves the complete RPC surface (transaction execution, point lookups, checkpoint subscriptions), but only within the full node's retention window.
-
The Archival Service is a separate gRPC service that serves unpruned point lookups for checkpoints, transactions, and objects. It is scoped to those point lookups, so it does not serve the rest of the full node RPC surface, such as transaction execution or live state queries. Reuse the same
LedgerServicemethods, but point the client at an Archival Service endpoint such asarchive.mainnet.sui.io:443or your provider's archival URL. See Archival Store. -
GraphQL is a unified interface that serves both recent data and, when the operator configures archival access, unpruned point lookups, with no client-side fallback.
Typed requests replace positional JSON-RPC params
JSON-RPC accepts a positional array of arguments. gRPC uses typed protobuf messages, and GraphQL uses named, typed arguments and selection sets. When you port a call, do not translate positional indices directly. Read the schema and pass each value by name, including the response shape selector. For gRPC, use a FieldMask to fetch only the fields you need rather than the entire response. See field masks for the rules.
WebSocket subscriptions are replaced by gRPC streaming
JSON-RPC offered WebSocket subscriptions for events and transactions. Sui does not carry those forward. Use SubscriptionService.SubscribeCheckpoints over gRPC server streaming to receive finalized checkpoints in order, and read each checkpoint's transactions, effects, and events from the streamed payload. If your stream disconnects, resume from the last processed checkpoint sequence number. Backfill any missed range using LedgerService before you resubscribe. See Subscriptions for streaming data.
Public load balancer URLs are not production endpoints
The public URLs at https://fullnode.<network>.sui.io and https://graphql.<network>.sui.io/graphql are rate-limited and intended for development and public-good access. Do not point a production exchange, indexer, or wallet backend at them. Run your own Sui full node or contract with a provider for a dedicated gRPC or GraphQL endpoint.
Object and coin queries behave differently
GraphQL and gRPC expose richer typing for objects and coins than JSON-RPC. A few practical points:
-
Coin selection. Replace
suix_getCoinswith an owned-objects query filtered by coin type:Address.objectswithfilter: { type: "0x2::coin::Coin<...>" }in GraphQL, orStateService.ListOwnedObjectswith the same type filter on gRPC. To list coins of every type (thesuix_getAllCoinscase), filter by the bare0x2::coin::Cointype without a type parameter. For transaction building, prefer gas smashing or address-balance gas payments rather than constructing coin lists by hand. -
Balances cover both representations. Read
Balance.totalBalanceto get the sum across coin objects and the address-balance accumulator. ReadBalance.coinBalanceandBalance.addressBalanceseparately to reconcile against your own ledger. -
Object and package versions. GraphQL can scrub through the version history of an object or package, which has no JSON-RPC analog. Use
Query.objectVersions, orIObject.objectVersionsAfterandIObject.objectVersionsBeforefrom an object, to page through an object's versions. UseQuery.packageVersionsfor the published versions of a package. -
Checkpoint-scoped consistency. GraphQL can pin a query to the chain state at a specific checkpoint through
Checkpoint.query, so a read that spans multiple types or multiple pages sees one consistent snapshot. JSON-RPC has no equivalent.
Pagination uses typed cursors
JSON-RPC cursors are opaque strings. GraphQL passes cursors through the after (or before) field on a connection, with the page size in first (or last). gRPC paging uses fields on the typed request and response messages. Do not try to reuse a JSON-RPC nextCursor value in a GraphQL or gRPC request. Fetch a fresh cursor from the new API and persist it as the resume point.
Retention windows
A full node retains a configurable window of recent data and prunes older history. Your application sees this as "not found" responses. For long-running queries, replays, or audits, plan to fall back to the Archival Service for data outside the full node's retention.
Examples for high-traffic paths
Each pattern below links to the canonical worked example and includes an inline snippet you can expand. The canonical docs remain the source of truth for the full request and response shapes.
Transaction lookup
-
GraphQL: Example 2 in Migrating to GraphQL from JSON-RPC shows the JSON-RPC
sui_getTransactionBlockcall and the GraphQLQuery.transactionequivalent side by side. -
gRPC: Use
LedgerService.GetTransactionwith aFieldMaskthat selects the response fields you need (effects, events, object changes). See Querying Data with gRPC.
gRPC and GraphQL examples
Look up a transaction over gRPC with grpcurl:
$ grpcurl -d '{ "digest": "J4NvV5iQZQFm1xKPYv9ffDCCPW6cZ4yFKsCqFUiDX5L4" }' <full node URL:port> sui.rpc.v2.LedgerService/GetTransaction
The equivalent GraphQL query:
query {
transaction(digest: "Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF") {
gasInput {
gasSponsor {
address
}
gasPrice
gasBudget
}
effects {
status
timestamp
checkpoint {
sequenceNumber
}
}
}
}
Balances
-
GraphQL: Query
Address.balancefor a single coin type orAddress.balancesfor all balances, both reached fromQuery.address. ReadBalance.totalBalance,Balance.coinBalance, andBalance.addressBalancetogether to reconcile. See Using Address Balances for a verified example. -
gRPC: Use
StateService.GetBalanceorStateService.ListBalances. See theGetBalancerequest reference and the worked example in Using Address Balances.
gRPC and GraphQL examples
Read a balance over gRPC with the Buf CLI:
$ buf curl --protocol grpc https://<full node URL>/sui.rpc.v2.StateService/GetBalance -d '{ "owner": "<ADDRESS>", "coin_type": "0x2::sui::SUI" }'
The equivalent GraphQL query:
query {
address(address: "0xe4ee9c157b5eb185c2df885bd7dcb4be493630a913f4b0e0c7e8ecf77930a878") {
balance(coinType: "0x2::sui::SUI") {
coinType {
repr
}
addressBalance
coinBalance
totalBalance
}
}
}
Coins
-
GraphQL: Example 3 in Migrating to GraphQL from JSON-RPC maps
suix_getCoinstoAddress.objectswithfilter: { type: "0x2::coin::Coin<0x2::sui::SUI>" }, including how cursor pagination changes. -
gRPC: Read coin objects through
StateService.ListOwnedObjectswith aCoin<T>type filter.
gRPC and GraphQL examples
List owned objects over gRPC with grpcurl:
$ grpcurl -d '{ "owner": "0x94096a6a54129234237759c66e6ef1037224fb3102a0ae29d33b490281c8e4d5" }' <full node URL:port> sui.rpc.v2.StateService/ListOwnedObjects
Filter for a coin type in GraphQL. Pass the page size in first and the pagination cursor in after:
query {
address(address: "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9") {
objects(
first: 3,
after: "vBIzCwAAAADMAQBQlGUkKZV2Gebvp5pASmcU0RJuY/VR9LbH+3ZED4EYyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAARjb2luAAAAAAAAAARDb2luAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA3N1aQAAAAAAAAADU1VJAAAAAAAAAAAB/////XBZUf9feyhb9cW+AOBraL10O9t0D9JAr+tIq2wXDJdl5/zP5w==",
filter: { type: "0x2::coin::Coin<0x2::sui::SUI>" }
) {
nodes {
address
}
}
}
}
Omit after to fetch the first page. The cursor is opaque, so use one returned by a previous query rather than constructing it by hand.
Events
-
GraphQL: See Querying events with GraphQL for the
events(filter:)field and the filtering reference. -
gRPC: Events arrive as part of transaction data in
LedgerService.GetTransactionresponses, and live updates arrive in each checkpoint streamed bySubscriptionService.SubscribeCheckpoints. See Querying events with RPC.
GraphQL example
Query events with a type filter:
query {
events(
filter: { type: "0x3164fcf73eb6b41ff3d2129346141bd68469964c2d95a5b1533e8d16e6ea6e13::Market::ChangePriceEvent<0x2::sui::SUI>" }
) {
nodes {
sender {
address
}
timestamp
contents {
type {
repr
}
json
}
}
}
}
Checkpoints
-
gRPC: Subscribe with
SubscriptionService.SubscribeCheckpointsfor an ordered, gapless stream of finalized checkpoints. UseLedgerService.GetCheckpointfor point lookups. See Subscriptions for streaming data. -
GraphQL: Query
Query.checkpointfor a specific checkpoint, or paginateQuery.checkpointsfor a range. UseCheckpoint.queryto scope a multi-resource read to a specific checkpoint.
gRPC and GraphQL examples
Look up a checkpoint over gRPC with grpcurl:
$ grpcurl -d '{ "sequence_number": "164329987", "read_mask": { "paths": ["transactions"]} }' <full node URL:port> sui.rpc.v2.LedgerService/GetCheckpoint
Subscribe to the checkpoint stream over gRPC with the Buf CLI:
$ buf curl --protocol grpc https://<full node URL>/sui.rpc.v2.SubscriptionService/SubscribeCheckpoints -d '{ "readMask": "sequenceNumber,digest,summary.timestamp" }' --timeout 1m
Look up a checkpoint in GraphQL:
query {
checkpoint(sequenceNumber: 164329987) {
digest
networkTotalTransactions
timestamp
}
}
Submit a transaction
Build the transaction with a PTB through the SDK, sign it, and execute it through TransactionExecutionService.ExecuteTransaction (gRPC) or the Mutation.executeTransaction mutation (GraphQL). See Building Programmable Transaction Blocks for the construction steps and Signing and Sending Transactions for the signature requirements.