Skip to main content

JSON-RPC Migration Guide

JSON-RPC is deprecated. Migrate to either gRPC or GraphQL RPC before the week of July 27, 2026, when JSON-RPC is disabled on Sui Foundation mainnet full nodes. Full decommission, including code removal, is planned for mid-October 2026. For a method mapping, decision criteria, and the full timeline, 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 being decommissioned on the following timeline:

MilestoneTarget date
Disable JSON-RPC on Sui Foundation's mainnet full nodesWeek of July 27, 2026
Sui Foundation stops publishing JSON-RPC snapshots for full nodesEnd of August 2026
Implicit fallback from JSON-RPC to archival service disconnectedEnd of September 2026
Full decommission of JSON-RPC from full nodes, including code removalMid-October 2026

The 2 replacements are gRPC for real-time full node access, archival service access and transaction execution, and GraphQL RPC for rich, structured queries over live and historical blockchain data.

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, transactions, and events with SubscriptionService. Use SubscribeCheckpoints, SubscribeTransactions, or SubscribeEvents with server-side filters to receive only the data your application needs.

  • Paginated, filtered queries over historical checkpoints, transactions, and events through the LedgerService list APIs (ListCheckpoints, ListTransactions, ListEvents). These APIs accept the same filter types as the subscription APIs, so you can backfill historical data and then continue from the same point with a live subscription.

  • Low-latency point lookups against the current state and recent history of a full node, through LedgerService and StateService.

  • 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 methodgRPC service and operationGraphQL field
sui_getCheckpoint, sui_getLatestCheckpointSequenceNumberLedgerService.GetCheckpointQuery.checkpoint
sui_getCheckpointsLedgerService.ListCheckpoints for paginated, filtered queries over a checkpoint range. Use SubscriptionService.SubscribeCheckpoints for an ordered live stream, or LedgerService.GetCheckpoint for single-checkpoint lookups.Query.checkpoints
sui_getTransactionBlockLedgerService.GetTransactionQuery.transaction
sui_multiGetTransactionBlocksLedgerService.BatchGetTransactionsQuery.multiGetTransactions
sui_getTotalTransactionBlocksLedgerService.GetCheckpoint on the latest or target checkpoint, then read summary.total_network_transactionsCheckpoint.networkTotalTransactions
sui_getObject, sui_tryGetPastObjectLedgerService.GetObject (current state and recent history)Query.object
sui_multiGetObjectsLedgerService.BatchGetObjectsQuery.multiGetObjects
suix_getBalance, suix_getAllBalancesStateService.GetBalance, StateService.ListBalancesAddress.balance, Address.balances
suix_getCoins, suix_getAllCoinsStateService.ListOwnedObjects with a type filter: 0x2::coin::Coin<T> for one coin type, or the bare 0x2::coin::Coin for all coin typesAddress.objects with filter: { type: "0x2::coin::Coin<T>" } for one coin type, or filter: { type: "0x2::coin::Coin" } for all coin types
suix_getCoinMetadataStateService.GetCoinInfoQuery.coinMetadata
suix_getOwnedObjectsStateService.ListOwnedObjectsAddress.objects
suix_getDynamicFieldsStateService.ListDynamicFieldsObject.dynamicFields
suix_getDynamicFieldObjectDerive 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_queryTransactionBlocksLedgerService.ListTransactions with a TransactionFilter for paginated, filtered queries over a checkpoint range. Use SubscriptionService.SubscribeTransactions with the same filter for a live stream of matching transactions.Query.transactions with a TransactionFilter
suix_queryEvents (deprecated)LedgerService.ListEvents with an EventFilter for paginated, filtered queries over a checkpoint range. Use SubscriptionService.SubscribeEvents with the same filter for a live stream. For a single transaction's events, use LedgerService.GetTransaction with events in the field mask. See Emitting Events for examples and migration guidance.Query.events with an EventFilter. See Emitting Events for examples.
sui_subscribeEvent, sui_subscribeTransaction (WebSocket)SubscriptionService.SubscribeEvents with an EventFilter, or SubscriptionService.SubscribeTransactions with a TransactionFilter. Use SubscriptionService.SubscribeCheckpoints if you need entire checkpoints. All 3 support server-side filtering.No streaming subscription. Poll Query.events with an EventFilter, or Query.transactions with a TransactionFilter.
suix_getReferenceGasPriceLedgerService.GetEpoch (read reference_gas_price from the response)Epoch.referenceGasPrice
suix_getStakes, suix_getStakesByIdsNo 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_getValidatorsApyNo 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_executeTransactionBlockTransactionExecutionService.ExecuteTransactionMutation.executeTransaction
sui_dryRunTransactionBlock, sui_devInspectTransactionBlockTransactionExecutionService.SimulateTransactionQuery.simulateTransaction
unsafe_paySui, unsafe_pay, unsafe_payAllSui, unsafe_transferSui, unsafe_transferObject, unsafe_moveCall, unsafe_splitCoin, unsafe_mergeCoins, unsafe_batchTransactionBuild a programmable transaction block with the SDK, then execute it through TransactionExecutionService.ExecuteTransactionBuild a PTB with the SDK, then submit through Mutation.executeTransaction
sui_getNormalizedMoveModule, sui_getNormalizedMoveFunction, sui_getNormalizedMoveStructMovePackageServiceQuery.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 3 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 LedgerService methods, but point the client at an Archival Service endpoint such as archive.mainnet.sui.io:443 or 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, which have been deprecated since mid-2024. Sui does not carry those forward. Use SubscriptionService over gRPC server streaming instead: SubscribeEvents with an EventFilter replaces sui_subscribeEvent, SubscribeTransactions with a TransactionFilter replaces sui_subscribeTransaction, and SubscribeCheckpoints delivers entire checkpoints. All 3 support server-side filtering. Subscriptions always begin at the current tip of the chain; they do not support resumption from a prior point. To avoid gaps, open the subscription first, note the first progress marker (cursor for checkpoints, Watermark for transactions and events), then backfill with the corresponding LedgerService list API (ListEvents, ListTransactions, or ListCheckpoints) up to that point. Once the backfill is complete, begin consuming from the already-open subscription. 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_getCoins with an owned-objects query filtered by coin type: Address.objects with filter: { type: "0x2::coin::Coin<...>" } in GraphQL, or StateService.ListOwnedObjects with the same type filter on gRPC. To list coins of every type (the suix_getAllCoins case), filter by the bare 0x2::coin::Coin type 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.totalBalance to get the sum across coin objects and the address-balance accumulator. Read Balance.coinBalance and Balance.addressBalance separately 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, or IObject.objectVersionsAfter and IObject.objectVersionsBefore from an object, to page through an object's versions. Use Query.packageVersions for 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

curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getTransactionBlock",
"params": [
"Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF",
{ "showEffects": true, "showEvents": true }
]
}'

Balances

# Single coin type
curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getBalance",
"params": ["0xADDRESS", "0x2::sui::SUI"]
}'

# All coin types
curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getAllBalances",
"params": ["0xADDRESS"]
}'

Coins

curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_getCoins",
"params": ["0xADDRESS", "0x2::sui::SUI", null, 50]
}'

Events

Filtered event queries (GraphQL only)

  • gRPC: Use LedgerService.ListEvents with an EventFilter for paginated historical queries. For live updates, use SubscriptionService.SubscribeEvents with the same filter types. Events also arrive as part of transaction data in LedgerService.GetTransaction responses. See Querying events with gRPC.
curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_queryEvents",
"params": [
{ "MoveEventType": "0xPACKAGE::module::MyEvent" },
null, 50, false
]
}'

Events from a known transaction

curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_queryEvents",
"params": [
{ "Transaction": "8NB8sXb4m9PJhCyLB7eVH4onqQWoFFzVUrqPoYUhcQe2" },
null, 50, false
]
}'

Checkpoints

curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getCheckpoint",
"params": ["164329987"]
}'

For real-time checkpoint streaming, use gRPC SubscriptionService.SubscribeCheckpoints. See Subscriptions for streaming data and the gRPC migration cookbook.

Submit a transaction

curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_executeTransactionBlock",
"params": [
"<BASE64_TX_BYTES>",
["<BASE64_SIGNATURE>"],
{ "showEffects": true }
]
}'