# JSON-RPC Migration Guide

*[Documentation index](/llms.txt) · [Full index](/llms-full.txt)*

**JSON-RPC is deprecated**. Migrate to either [gRPC](/develop/accessing-data/grpc) or [GraphQL RPC](/develop/accessing-data/graphql/graphql-rpc) by July 2026. For a method mapping, decision criteria, and common migration gotchas, see the [JSON-RPC Migration Guide](/develop/accessing-data/json-rpc-migration).

Refer to the [list of RPC or data providers](https://www.notion.so/mystenlabs/RPC-providers-offering-future-Sui-data-primitives-2466d9dcb4e980a99a36e9aafd8c17e0?source=copy_link) 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](/develop/accessing-data/grpc) for full node access and transaction execution, and [GraphQL RPC](/develop/accessing-data/graphql/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 `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](/develop/accessing-data/grpc/what-is-grpc) and [GraphQL RPC](/develop/accessing-data/graphql/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](/references/fullnode-protocol). 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](https://github.com/MystenLabs/sui/issues/23832) for the recommended computation. | No direct APY field. Read validator-set data through `Epoch.validatorSet` and compute APY client-side. See [issue #23832](https://github.com/MystenLabs/sui/issues/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](/develop/transactions/ptbs/prog-txn-blocks) 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](/develop/transactions/ptbs/prog-txn-blocks) using the [TypeScript SDK](https://sdk.mystenlabs.com/typescript) or the [Rust SDK](https://github.com/MystenLabs/sui-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 `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](/develop/accessing-data/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](/develop/accessing-data/grpc/using-grpc#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](/develop/accessing-data/grpc/what-is-grpc#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](/operators/full-node/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](/develop/transaction-payment/gas-smashing) or [address-balance gas payments](/onchain-finance/asset-custody/address-balances/using-address-balances) 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](/develop/accessing-data/archival-store) 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](/develop/accessing-data/graphql/query-with-graphql#migrating-to-graphql-from-json-rpc) shows the JSON-RPC `sui_getTransactionBlock` call and the GraphQL `Query.transaction` equivalent side by side.

- **gRPC:** Use `LedgerService.GetTransaction` with a `FieldMask` that selects the response fields you need (effects, events, object changes). See [Querying Data with gRPC](/develop/accessing-data/grpc/using-grpc).

**gRPC and GraphQL examples**

Look up a transaction over gRPC with `grpcurl`:

```sh
$ grpcurl -d '{ "digest": "J4NvV5iQZQFm1xKPYv9ffDCCPW6cZ4yFKsCqFUiDX5L4" }' <full node URL:port> sui.rpc.v2.LedgerService/GetTransaction
```

The equivalent GraphQL query:

```graphql
query {
  transaction(digest: "Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF") {
    gasInput {
      gasSponsor {
        address
      }
      gasPrice
      gasBudget
    }
    effects {
      status
      timestamp
      checkpoint {
        sequenceNumber
      }
    }
  }
}
```

### Balances

- **GraphQL:** Query `Address.balance` for a single coin type or `Address.balances` for all balances, both reached from `Query.address`. Read `Balance.totalBalance`, `Balance.coinBalance`, and `Balance.addressBalance` together to reconcile. See [Using Address Balances](/onchain-finance/asset-custody/address-balances/using-address-balances#querying-balances) for a verified example.

- **gRPC:** Use `StateService.GetBalance` or `StateService.ListBalances`. See the [`GetBalance` request reference](/references/fullnode-protocol-messages#sui-rpc-v2-GetBalanceRequest) and the worked example in [Using Address Balances](/onchain-finance/asset-custody/address-balances/using-address-balances#querying-balances).

**gRPC and GraphQL examples**

Read a balance over gRPC with the Buf CLI:

```sh
$ 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:

```graphql
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](/develop/accessing-data/graphql/query-with-graphql#migrating-to-graphql-from-json-rpc) maps `suix_getCoins` to `Address.objects` with `filter: { type: "0x2::coin::Coin<0x2::sui::SUI>" }`, including how cursor pagination changes.

- **gRPC:** Read coin objects through `StateService.ListOwnedObjects` with a `Coin<T>` type filter.

**gRPC and GraphQL examples**

List owned objects over gRPC with `grpcurl`:

```sh
$ 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`:

```graphql
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](/develop/accessing-data/using-events#querying-events-with-graphql) for the `events(filter:)` field and the filtering reference.

- **gRPC:** Events arrive as part of transaction data in `LedgerService.GetTransaction` responses, and live updates arrive in each checkpoint streamed by `SubscriptionService.SubscribeCheckpoints`. See [Querying events with RPC](/develop/accessing-data/using-events#querying-events-with-rpc).

**GraphQL example**

Query events with a type filter:

```graphql
query {
  events(
    filter: { type: "0x3164fcf73eb6b41ff3d2129346141bd68469964c2d95a5b1533e8d16e6ea6e13::Market::ChangePriceEvent<0x2::sui::SUI>" }
  ) {
    nodes {
      sender {
        address
      }
      timestamp
      contents {
        type {
          repr
        }
        json
      }
    }
  }
}
```

### Checkpoints

- **gRPC:** Subscribe with `SubscriptionService.SubscribeCheckpoints` for an ordered, gapless stream of finalized checkpoints. Use `LedgerService.GetCheckpoint` for point lookups. See [Subscriptions for streaming data](/develop/accessing-data/grpc/what-is-grpc#subscriptions-for-streaming-data).

- **GraphQL:** Query `Query.checkpoint` for a specific checkpoint, or paginate `Query.checkpoints` for a range. Use `Checkpoint.query` to scope a multi-resource read to a specific checkpoint.

**gRPC and GraphQL examples**

Look up a checkpoint over gRPC with `grpcurl`:

```sh
$ 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:

```sh
$ 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:

```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](/develop/transactions/ptbs/building-ptb) for the construction steps and [Signing and Sending Transactions](/develop/transactions/transaction-auth/auth-overview) for the signature requirements.
