# Querying Data with GraphQL RPC

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

This guide provides practical examples for querying the Sui network using GraphQL, including reads, transaction submission, and transaction simulation. For core concepts like headers, variables, fragments, pagination, scope, and limits, see the [corresponding concepts page](/develop/accessing-data/graphql/graphql-rpc).

Access the GraphQL service for Sui RPC through an online IDE that provides a complete toolbox for fetching data and executing transactions on the network:

- Mainnet: https://graphql.mainnet.sui.io/graphql
- Testnet: https://graphql.testnet.sui.io/graphql
- Devnet: https://graphql.devnet.sui.io/graphql

:::info

The public Mainnet and Testnet GraphQL services are rate-limited to keep network throughput optimized. They are useful for development and public-good access, but production apps should use a provider endpoint or operate their own GraphQL stack. 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 support GraphQL RPC or gRPC.

:::

The online IDE provides features such as auto-completion (use <kbd>Ctrl</kbd>+<kbd>Space</kbd> or just start typing), built-in documentation (Book icon, top-left), multi-tabs, and more. You can try the example queries on this page directly in the IDE.

## Supported schema

GraphQL introspection exposes the schema supported by the RPC service. The IDE's **Docs** pane (Book icon, top-left) and **Search** dialog (<kbd>Cmd</kbd> + <kbd>K</kbd> on macOS or <kbd>Ctrl</kbd> + <kbd>K</kbd> on Windows and Linux) offer a way to browse introspection output interactively.

Consult the [GraphQL reference](/references/sui-graphql) for full documentation on the supported schema. The [official GraphQL introspection documentation](https://graphql.org/learn/introspection/) provides an overview on introspection and how to interact with it programmatically.

## Headers

The service accepts the following optional HTTP request headers:

- `x-sui-rpc-version`: Specifies which RPC version to use. Currently only one version is supported.
- `x-sui-rpc-show-usage`: Returns the response with extra query complexity information.

By default, each response contains the following HTTP response headers:

- `x-sui-rpc-request-id`: A unique identifier for the request. This appears in application logs for debugging.
- `x-sui-rpc-version`: The version of the service that handled the request.

```sh
$ curl -i -X POST https://graphql.testnet.sui.io/graphql\
     --header 'x-sui-rpc-show-usage: true'                 \
     --header 'Content-Type: application/json'             \
     --data '{
          "query": "query { epoch { referenceGasPrice } }"
     }'
```

**Output**

```sh
HTTP/2 200
content-type: application/json
content-length: 179
x-sui-rpc-request-id: f5442058-47ab-4360-8295-61c009f38516
x-sui-rpc-version: x.y.z
vary: origin, access-control-request-method, access-control-request-headers
access-control-allow-origin: *
date: Tue, 09 Sep 2025 23:34:04 GMT
via: 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

{
  "data": {
    "epoch": {
      "referenceGasPrice": "1000"
    }
  },
  "extensions": {
    "usage": {
      "input": {
        "nodes": 2,
        "depth": 2
      },
      "payload": {
        "query_payload_size": 67,
        "tx_payload_size": 0
      },
      "output": {
        "nodes": 2
      }
    }
  }
}
```

## Variables

Variables offer a way to introduce dynamic inputs to a re-usable or static query. Declare variables in the parameters to a `query` or `mutation`, using the `$` symbol and its type (in this example `Int`), which must be a `scalar`, `enum`, or `input` type. In the query body, refer to it by its name prefixed with the `$` symbol.

If you declare a variable but don't use it, or define it in the query, the query fails to execute. To learn more, read the GraphQL documentation on [variables](https://graphql.org/learn/queries/#variables).

In the following example, a variable supplies the ID of the epoch being queried:

```graphql
query ($epochID: Int) {
  epoch(id: $epochID) {
    referenceGasPrice
  }
}
```

**Variables**:
```json
{
   "epochID": 100
}
```

#### Within the GraphQL IDE

When using the [GraphQL online integrated development environment (IDE)](https://graphql.mainnet.sui.io/graphql), supply variables as a JSON object to the query in the **Variables** pane at the bottom of the main editing window. You receive a warning if you supply a variable but don't declare it.

#### Within requests

When making a request to the GraphQL service using a tool such as `curl`, pass the query and variables as two fields of a single JSON object:

```sh
$ curl -X POST https://sui-testnet.mystenlabs.com/graphql \
    --header 'Content-Type: application/json' \
    --data '{
      "query": "query ($epochID: Int) { epoch(id: $epochID) { referenceGasPrice } }",
      "variables": { "epochID": 100 }
  }'
```

## Fragments

Fragments are reusable units that you can include in queries as needed. To learn more, consult the official [GraphQL documentation](https://graphql.org/learn/queries/#fragments). The following example uses fragments to factor out a reusable snippet representing a Move value:

```graphql
query DynamicField {
  object(
    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
  ) {
    dynamicField(
      name: {
        type: "0x2::kiosk::Lock",
        bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1Vhfw=",
      }
    ) {
      ...DynamicFieldSelect
    }
  }
}

fragment DynamicFieldSelect on DynamicField {
  name {
    ...MoveValueFields
  }
  value {
    ...DynamicFieldValueSelection
  }
}

fragment DynamicFieldValueSelection on DynamicFieldValue {
  __typename
  ... on MoveValue {
    ...MoveValueFields
  }
  ... on MoveObject {
    hasPublicTransfer
    contents {
      ...MoveValueFields
    }
  }
}

fragment MoveValueFields on MoveValue {
  type {
    repr
  }
  json
  bcs
}
```

## Pagination

GraphQL supports queries that fetch multiple kinds of potentially nested data. For example, the following query retrieves the first 10 transactions in epoch `97` along with the digest, sender's address, gas object returned after paying for the transaction, gas price, and gas budget:

```graphql
query {
  epoch(epochId: 97) {
    transactions(first: 10) {
      pageInfo {
        hasNextPage
        endCursor
      }
      nodes {
        digest
        sender {
          address
        }
        effects {
          gasEffects {
            gasObject {
              address
            }
          }
        }
        gasInput {
          gasPrice
          gasBudget
        }
      }
    }
  }
}
```

If there are too many transactions to return in a single response, the service applies a [limit](#limits) on the maximum page size for variable size responses (like the `transactionBlock` query) and you must fetch further results through [pagination](https://graphql.org/learn/pagination/).

### Connections

Fields that return a paginated response accept the following optional parameters:

- `first`: Limit on page size that is met by dropping excess results from the end.
- `after`: Cursor that bounds the results from below, exclusively.
- `last`: Limit on page size that is met by dropping excess results from the start.
- `before`: Cursor that bounds the results from above, exclusively.

They also return a type that conforms to the [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm), meaning its name ends in `Connection`, and it contains at minimum the following fields:

- `pageInfo` of type `PageInfo`, which indicates whether there are more pages before or after the page returned.
- `nodes`: The content of the paginated response as a list of the type being paginated (`TransactionBlock` in the previous example).
- `edges`: Similar to `nodes` but associating each node with its [cursor](#cursors).

### Cursors

Cursors are opaque identifiers for paginated results. The only valid source for a cursor parameter (like `after` and `before`) is a cursor field from a previous paginated response (like `PageInfo.startCursor`, `PageInfo.endCursor`, or `Edge.cursor`). The underlying format of the cursor is an implementation detail, and is not guaranteed to remain fixed across versions of the GraphQL service, so do not rely on it. Generating cursors out of thin air is not expected or supported.

Cursors are used to bound results from below (with `after`) and above (with `before`). In both cases, the bound is exclusive, meaning it does not include the result that the cursor points to in the bounded region.

#### Consistency

Cursors for queries that paginate live objects also guarantee **consistent** pagination. They encode the checkpoint at which the query was first executed so that later pages are [scoped](#scope) by the same checkpoint, even if newer checkpoints are available. If both an `after` and a `before` cursor are provided, they must both be from the same checkpoint, otherwise the query produces an error.

By default, RPCs offer roughly 1 hour of [retention](#retention) for consistent pagination.

### Page limits

After results are bounded using cursors, a page size limit is applied using the `first` and `last` parameters. The service requires these parameters to be less than or equal to the max page size [limit](#limits), and if you provide neither, it selects a default. In addition to setting a limit, `first` and `last` control where excess elements are discarded from. For example, if there are 10 potential results (R0, R1, through R9) after cursor bounds have been applied, then:

- A limit of `first: 3` would select `R0`, `R1`, `R2`.
- A limit of `last: 3` would select `R7`, `R8`, `R9`.

:::info

It is an error to apply both a `first` and a `last` limit.

:::

## Scope

GraphQL requests are evaluated in a **scope** that controls the checkpoint being viewed. The GraphQL service responds to queries as if this is the last checkpoint to have been executed. By default, this is set to the latest checkpoint that the service has all data for. To avoid returning partial responses, the service does not allow queries to specify a later checkpoint than this, but it can be set to an earlier checkpoint to perform historical queries.

Optionally, the scope provides a **root object** bound that applies only to queries that fetch dynamic fields. The query fetches dynamic fields as they existed at the end of a specific checkpoint or when their root object reached a given version. For any wrapped or child (object-owned) object, the root object is defined recursively as:

- The root object of the object it is wrapped in, if it is wrapped
- The root object of its owner, if it is owned by another object
- The object itself

If a dynamic field's root object has version `v`, its own version, `w` is the latest version such that `w <= v` meaning the latest version of the dynamic field that existed when its root object was at version `v`.

If a root object bound is not provided, dynamic fields are fetched at the checkpoint being viewed, while if a checkpoint-based root object bound exists, it does not impact the checkpoint being viewed.

Finally, the GraphQL service treats queries nested under executed `Mutation.executeTransaction` and simulated `Query.simulateTransaction` transactions as being in a special scope that exists just after the transaction that was executed or simulated, without having indexed that transaction. This means execution-attached or simulation-attached queries can often provide consistent read-after-write results immediately, as long as the requested fields do not require indexed history.

The scope a query is evaluated in impacts which fields are available. In particular:

- Live object set queries are not available under executed transaction scopes or when a root object binding specifies a particular version. These queries rely on data that is indexed at the checkpoint level.
- Queries that paginate through history are not available under executed transaction scopes. Before indexing occurs, the system cannot determine where in the history the transaction falls.
- If you only need data returned by transaction execution or simulation effects, prefer selecting it in the same GraphQL request instead of submitting the transaction and then waiting for a separately indexed follow-up query.

### Setting checkpoint scope

You can run queries against a historical checkpoint using `Checkpoint.query`. For [consistent](#consistency) live object set queries (such as fetching an address' owned objects or balances, or an object's dynamic fields), you can continue to paginate using a cursor obtained from a previous query at that checkpoint.

```graphql
query AtCheckpoint($cp: UInt53!) {
  checkpoint(sequenceNumber: $cp) {
    query {
      transactions(last: 5) {
        nodes {
          digest
        }
      }
    }
  }
}

query NextBalancesPage($address: SuiAddress!, $after: String!) {
  address(address: $address) {
    balances(after: $after, first: 10) {
      pageInfo {
        hasNextPage
        endCursor
      }
      nodes {
        objectId
        balance
      }
    }
  }
}
```

`AtCheckpoint` returns the last 5 transactions to execute as of the end of the checkpoint with sequence number `$cp`, while `NextBalancesPage` fetches the next 10 balances for `$address` after the page that ended at cursor `$after`. You can also combine both approaches, to ensure that the first page of a live object query is fetched from a specific checkpoint:

```graphql
query FirstBalancesPageAtCheckpoint($address: SuiAddress!, $cp: UInt53) {
  checkpoint(sequenceNumber: $cp) {
    query {
      address(address: $address) {
        balances(first: 10) {
          pageInfo {
            hasNextPage
            endCursor
          }
          nodes {
            objectId
            balance
          }
        }
      }
    }
  }
}
```

Responses to these queries are subject to [retention](#retention). If `$cp` is outside the retention window `Query.transactions`, `AtCheckpoint` returns no results, while an attempt to continue paginating `NextBalancesPage` at a checkpoint outside the consistent range returns an error.

### Setting root version scope

Queries nested under the fetch of an object at a specific version are scoped by a root object bound at its version. For example, in the following query, the dynamic field with name `42u64` is fetched as it existed when its owning object was at version `$v`:

```graphql
query ObjectsDynamicFields($id: SuiAddress!, $v: UInt53!) {
  object(address: $id, version: $v) {
    dynamicField(name: { literal: "42u64" }) {
      value {
        ... on MoveValue {
          json
        }
      }
    }
  }
}
```

This property applies recursively to nested dynamic field queries. It also applies if the root object is fetched at a specific checkpoint using the `atCheckpoint` parameter or at the latest checkpoint (by omitting all parameters). In these cases, the root object bound is checkpoint-based.

The `rootVersion` parameter overrides this implicit bound. It fetches the object as if it was subject to a root version bound governed by the parameter, which also applies to queries nested underneath it. This is necessary when fetching a dynamic field directly (as an object), because an object's version is not updated when its children are updated unless it is a root object.

In the following query, `$id` is the address of a dynamic field fetched as it existed when its root object was at version `$r`. The nested query fetches a nested dynamic field with name `42u64` owned by a wrapped object stored in field `foo` of the dynamic field. The nested fetch is subject to the same root object version bound, `$r`.

```graphql
query NestedDynamicFields($id: SuiAddress!, $r: UInt53!) {
  object(address: $id, rootVersion: $v) {
    asMoveObject {
      asDynamicField {
        value {
          ... on MoveValue {
            extract(path: "foo->[42u64]") {
              json
            }
          }
        }
      }
    }
  }
}
```

Queries that return an `Address` can be used to make nested dynamic field queries on wrapped objects. An `Address` does not have an associated version, but `Query.address` can accept `rootVersion` or `atCheckpoint` parameters to set root object bounds for nested dynamic field queries. The following query fetches a dynamic field with name `42u64` owned by an object with address `$id` (which can belong to a wrapped object) as it existed when its root object was at version `$r`:

```graphql
query WrappedDynamicField($id: SuiAddress!, $r: UInt53!) {
  address(address: $id, rootVersion: $r) {
    dynamicField(name: { literal: "42u64" }) {
      value {
        ... on MoveValue {
          json
        }
      }
    }
  }
}
```

While nested within a scope that has a root object bound, you can reset or override the bound using the `objectAt` or `addressAt` queries. These fields query the same entity but at a different position in its history. If these fields are not provided any parameters, they fetch the state of the object at the latest known checkpoint to the GraphQL service. The following query fetches the latest version of an object that was previously fetched at version `$v`:

```graphql
query LatestDynamicField($id: SuiAddress!, $v: UInt53!) {
  object(address: $id, version: $v) {
    objectAt {
      version
    }
  }
}
```

## Limits

The GraphQL service for Sui RPC is rate-limited on all available instances to keep network throughput optimized and to protect against excessive or abusive calls to the service.

### Rate limits

Queries are rate-limited at the number of attempts per minute to ensure high availability of the service to all users.

### Query limits

In addition to rate limits, queries are also validated against a number of rules on their complexity, such as the number of nodes, the depth of the query, or their payload size. Query the `serviceConfig` field to retrieve these limits. An example of how to query for some of the available limits follows:

```graphql
{
  serviceConfig {
    maxQueryDepth
    maxQueryNodes
    maxOutputNodes
    defaultPageSize(type: "Query", field: "transactions")
    maxPageSize(type: "Query", field: "objects")
    queryTimeoutMs
    maxQueryPayloadSize
    maxTypeArgumentDepth
    maxTypeArgumentWidth
    maxTypeNodes
    maxMoveValueDepth
  }
}
```

### Rich queries

Rich queries require a dedicated request to a backing store. There is a limit on the number of rich queries you can perform in a single GraphQL request, which can also be queries from the `serviceConfig`:

```graphql
{
  serviceConfig {
    maxRichQueries
  }
}
```

If a request performs more rich queries than the allowed limit, excess rich queries are arbitrarily replaced by a `"RESOURCE_EXHAUSTED"` error. Rich queries include:

- Paginating owned object queries, such as `IAddressable.objects`, `IAddressable.balances`, and `IMoveObject.dynamicFields`.
- Paginating historical queries, such as `Query.transactions`, and `Query.events`.
- Paginating through versions of packages and objects, such as `Query.objectVersions`, `IObject.objectVersionsAfter`, or `IObject.objectVersionsBefore`.

## Retention

Different queries are subject to their own retention policies. In the vanilla, general purpose setup, live object set queries work only for recent checkpoints (measured in minutes or hours), while transaction pagination might be available only back to certain checkpoints (measured in weeks or months), depending on the filters applied. Data outside these ranges is pruned.

### Data source retention

GraphQL queries rely on different data sources with different retention ranges. When the GraphQL operator configures Archival Service, supported point lookups can route to Archival. Full node gRPC does not do this implicitly; gRPC clients must query an Archival Service endpoint directly for high-retention data.

| Data source          | Examples                                                  | Typical retention          |
| -------------------- | --------------------------------------------------------- | -------------------------- |
| **Consistent store** | `Query.objects`, `Address.balance`, objects by owner or type | ~1 hour                    |
| **Database store**   | `Query.transactions`, `Query.events`, `Query.checkpoints` | ~(30-90) days              |
| **Archival service** | Point lookups (transaction by digest, object by ID and version)        | Indefinite (if configured) |

:::tip Operators

Retention is dependent on the pruning policies of datasource pipelines configured by the stack operator. For production configuration examples, see [Indexer Stack Setup](/operators/data-management/indexer-stack-setup).

:::

### Querying available range

Query the available checkpoint range using `serviceConfig.availableRange`:

```graphql
availableRange(type: String!, field: String, filters: [String!]): AvailableRange!
```

- **`type`**: The GraphQL type name, such as `"Query"` or `"Address"`.
- **`field`**: The field on that type, such as `"transactions"` or `"events"`.
- **`filters`**: Filter field names from the filter input type, such as `["affectedAddress"]` for `TransactionFilter`, `["module"]` for `EventFilter`, or direct query parameter names like `["version"]`.

Match your query parameters to filter names: if your query uses `filter: { affectedAddress: "0x..." }`, pass `filters: ["affectedAddress"]`.

:::info Strictest retention is returned by the available range query.
Datasource pipelines can be configured with different retention ranges based on pruning configurations.
When multiple filters are passed to the available range query, the strictest retention range of the pipelines that support the filters is returned.
:::

### Examples

```graphql
# Check available range for transaction queries filtered by affected address
{
	serviceConfig {
		availableRange(type: "Query", field: "transactions", filters: ["affectedAddress"]) {
			first {
				sequenceNumber
			}
			last {
				sequenceNumber
			}
		}
	}
}
```

```graphql
# Check available range for a checkpoint transactions queries filtered by affected object
{
	serviceConfig {
		availableRange(type: "Checkpoint", field: "Transactions", filters: ["affectedObject"]) {
			first {
				sequenceNumber
			}
			last {
				sequenceNumber
			}
		}
	}
}
```

:::info Nested queries

Each field in a nested query has its own retention limit:

```graphql
query {
	address(address: "0x...") {
		# Uses Consistent Store (~1 hour retention)
		dynamicFields(first: 10) {
			nodes {
				value {
					... on MoveObject {
						# Uses DB Store (may have different retention)
						objectAt(checkpoint: 10579000) {
							version
						}
					}
				}
			}
		}
	}
}
```

Use `serviceConfig.availableRange` to check retention for each field separately.

:::

### Handling "Outside available range" errors

If you encounter an error indicating data is outside the available range, it means your query is requesting data that has been pruned. To resolve this:

1. **Check the available range first** using `serviceConfig.availableRange` before starting long pagination runs.
2. **Use fresh cursors**. Old cursors might reference checkpoints that have since been pruned.
3. **Adjust your query bounds**. Ensure `afterCheckpoint`/`beforeCheckpoint` filters fall within the available range.

## Example queries

The following queries show some common tasks you can perform with GraphQL.

### Find the reference gas price for latest epoch

```graphql
query {
  epoch {
    referenceGasPrice
  }
}
```

### Find a specific historical epoch

Find the total stake rewards, the reference gas price, the number of checkpoints, and the total gas fees for epoch 100. In the query, the `epochId` argument is optional and defaults to the latest epoch.

```graphql
query {
  epoch(epochId: 100)
  {
    epochId
    totalStakeRewards
    referenceGasPrice
    totalCheckpoints
    totalGasFees
    totalStakeSubsidies
    storageFund {
      totalObjectStorageRebates
      nonRefundableBalance
    }
  }
}
```

### Find a transaction by its digest

Get a transaction by its digest and show information such as the gas sponsor address, the gas price, the gas budget, and the effects from executing that transaction.

```graphql
query {
  transaction(digest: "FdK...qK2") {
    gasInput {
      gasSponsor {
        address
      }
      gasPrice
      gasBudget
    }
    effects {
      status
      timestamp
      checkpoint {
        sequenceNumber
      }
      epoch {
        epochId
        referenceGasPrice
      }
    }
  }
}
```

### Find the last 10 transactions that are not a system transaction

```graphql
query {
  transactions(last: 10, filter: {kind: PROGRAMMABLE_TX}) {
    nodes {
      digest
      kind {
        __typename
      }
    }
  }
}
```

### Find all transactions that touched a given object

Find all the transactions that touched (modified, transferred, or deleted) a given object. This might be useful when you want to trace the flow of a `Coin`, `StakeSui`, `NFT`, or similar object.

:::info

This example uses GraphQL [variables](#variables) and [pagination](#pagination).

:::

```graphql
query ($objectID: SuiAddress!) {
  transactions(filter: {affectedObject: $objectID}) {
    nodes {
      sender {
        address
      }
      digest
      effects {
        objectChanges {
          nodes {
            address
          }
        }
      }
    }
  }
}
```

#### Variables

When using the online IDE, copy the following JSON to the **Variables** window, below the main editor.

```json
{
  "objectID": "0x11c6ae8432156527fc2e12e05ac7db79f2e972510a823a4ef2e670f27ad7b52f"
}
```

### Fetch typed object changes from a transaction

Use `effects.objectChanges` to inspect the before and after state for objects changed by a transaction. The `inputState` field represents the object before the transaction, while `outputState` represents the object after the transaction. To read the typed Move object contents, select `asMoveObject { contents { json } }` from either state.

```graphql
query ($digest: String!) {
  transaction(digest: $digest) {
    effects {
      objectChanges {
        nodes {
          address
          idCreated
          idDeleted
          inputState {
            asMoveObject {
              contents { json }
            }
          }
          outputState {
            asMoveObject {
              contents { json }
            }
          }
        }
      }
    }
  }
}
```

#### Variables

```json
{
  "digest": "<TRANSACTION_DIGEST>"
}
```

`inputState` is `null` for newly created objects, and `outputState` is `null` for deleted or wrapped objects. `asMoveObject` is `null` when the changed object is not a Move object, such as a package.

### Find Publisher objects created by a publish transaction

A package publish transaction often creates a `0x2::package::Publisher` object. Filter `objectChanges` for output objects with that Move type to find the Publisher object ID and its typed contents.

```graphql
query ($digest: String!) {
  transaction(digest: $digest) {
    effects {
      objectChanges {
        nodes {
          address
          outputState {
            asMoveObject {
              contents {
                type {
                  repr
                }
                json
              }
            }
          }
        }
      }
    }
  }
}
```

Look for changes where `outputState.asMoveObject.contents.type.repr` is `0x0000000000000000000000000000000000000000000000000000000000000002::package::Publisher`, the canonical form of `0x2::package::Publisher`. If your code displays abbreviated type strings, normalize before comparing or compare the `::package::Publisher` suffix. The object change `address` is the Publisher object ID.

### Filter transactions by a function

Find the last 10 transactions that called the `public_transfer` function as a Move call transaction command.

:::info

This example makes use of the `last` filter. In this case, it returns only the last 10 transactions known to the service.

:::

```graphql
{
  transactions(
    last: 10,
      filter: {
        function: "0x2::transfer::public_transfer"
      }
  ) {
    nodes { digest }
  }
}
```

### Find transaction balance changes

Find the balance changes of all the transactions where a given address called a staking-related function. This might be useful when you want to get your staking or unstaking history.

```graphql
query ($address: SuiAddress!) {
  transactions(filter: {
    function: "0x3::sui_system::request_add_stake"
    sentAddress: $address
  }) {
    nodes {
      digest
      effects {
        balanceChanges {
          nodes {
            owner {
              address
            }
            amount
          }
        }
      }
    }
  }
}
```

#### Variables

When using the online IDE, copy the following JSON to the **Variables** window, below the main editor.

```json
{
  "address": "0xa9ad44383140a07cc9ea62d185c12c4d9ef9c6a8fd2f47e16316229815862d23"
}
```

### Fetch a dynamic field on an object

:::info

This example uses aliases and [fragments](#fragments).

:::

```graphql
query DynamicField {
  object(
    address: "0xb57fba584a700a5bcb40991e1b2e6bf68b0f3896d767a0da92e69de73de226ac"
  ) {
    dynamicField(
      name: {
        type: "0x2::kiosk::Lock",
        bcs: "NLArx1UJguOUYmXgNG8Pv8KbKXLjWtCi6i0Yeq1Vhfw=",
      }
    ) {
      ...DynamicFieldSelect
    }
  }
}

fragment DynamicFieldSelect on DynamicField {
  name {
    ...MoveValueFields
  }
  value {
    ...DynamicFieldValueSelection
  }
}

fragment DynamicFieldValueSelection on DynamicFieldValue {
  __typename
  ... on MoveValue {
    ...MoveValueFields
  }
  ... on MoveObject {
    hasPublicTransfer
    contents {
      ...MoveValueFields
    }
  }
}

fragment MoveValueFields on MoveValue {
  type {
    repr
  }
  json
  bcs
}
```

### Fetch all dynamic fields on an object

Paginate over the dynamic fields of an object. It can be used for iterating over the elements of onchain data structures, like tables and bags. See [The Move Book](https://move-book.com/programmability/dynamic-collections) to learn more about dynamic collections available in Move.

:::info

This example uses [fragments](#fragments) and [variables](#variables).

:::

```graphql
query ($id: SuiAddress!) {
  address(address: $id) {
    dynamicFields {
      nodes {
        name { ...Value }
        value {
          __typename
          ... on MoveValue {
            ...Value
          }
          ... on MoveObject {
            contents {
              ...Value
            }
          }
        }
      }
    }
  }
}

fragment Value on MoveValue {
  type {
    repr
  }
  json
}
```

### Paginate checkpoints forward

Set up a paginated query starting at the genesis checkpoint, reading 5 checkpoints at a time in increasing order of sequence number. The value of `pageInfo.hasNextPage` determines whether there is another page to read. The value of `pageInfo.endCursor` is used as the cursor to read `$after`.

:::info

This example uses GraphQL [variables](#variables) and [pagination](#pagination).

:::

```graphql
query ($after: String) {
  checkpoints(first: 5, after: $after) {
    pageInfo {
      hasNextPage
      endCursor
    }
    nodes {
      digest
      timestamp
    }
  }
}
```

### Paginate checkpoints backward

Set up a paginated query starting at the latest indexed checkpoint, reading 5 checkpoints at a time in decreasing order of sequence number. The value of `pageInfo.hasPreviousPage` determines whether there is another page to read. The value of `pageInfo.startCursor` is used as the cursor to read `$before`.

:::info

This example uses GraphQL [variables](#variables) and [pagination](#pagination).

:::

```graphql
query ($before: String) {
  checkpoints(last: 5, before: $before) {
    pageInfo {
      hasPreviousPage
      startCursor
    }
    nodes {
      digest
      timestamp
    }
  }
}
```

### Execute a transaction

GraphQL is not read-only. Transaction execution takes 2 arguments, `transactionDataBcs` and `signatures`. `transactionDataBcs` is the serialized unsigned transaction data, which the [Sui Client CLI](/references/cli/client) `client call` command can generate. Pass the `--serialize-unsigned-transaction` flag to the command to call a Move function. The [Sui Keytool CLI](/references/cli/keytool) command `sui keytool sign` can generate the `signatures`. Select fields from `effects` in the same mutation to read execution results immediately without waiting for a separate indexed query.

```graphql
mutation ($tx: String!, $sigs: [String!]!) {
  executeTransaction(transactionDataBcs: $tx, signatures: $sigs) {
    errors
    effects {
      status
      epoch {
        startTimestamp
      }
      gasEffects {
        gasSummary {
          computationCost
        }
      }
    }
  }
}
```

#### Variables

When using the online IDE, copy the following JSON to the **Variables** window, below the main editor.

```json
{
  "tx": "AAACACAZXApmrHgzTs3FGDyXWka+wmMCy2IwOdKLmTWHb5PnFQEASlCnLAw4qfzLF3unH9or5/L7YpOlReaSEWfoEwhTqpavSxAAAAAAACCUFUCOn8ljIxcG9O+CA1bzqjunqr4DLDSzSoNCkUvu2AEBAQEBAAEAALNQHmLi4jgC5MuwwmiMvZEeV5kuyh+waCS60voE7fpzAa3v/tOFuqDvQ+bjBpKTfjyL+6yIg+5eC3dKReVwghH/rksQAAAAAAAgxtZtKhXTr1zeFAo1JzEqVKn9J1H74ddbCJNVZGo2I1izUB5i4uI4AuTLsMJojL2RHleZLsofsGgkutL6BO36c+gDAAAAAAAAQEIPAAAAAAAA",
  "sigs": [
    "AB4ZihXxUMSs9Ju5Cstuuf/hvbTvvycuRk2TMuagLYNJgQuAeXmKyJF9DAXUtL8spIsHrDQgemn4NmojcNl8HQ3JFqhnaTC8gMX4fy/rGgqgL6CDcbikawUUjC4zlkflwg=="
  ]
}
```

### Simulate a transaction

Use `simulateTransaction` to preview transaction effects, estimate gas, or test transaction logic without committing the transaction onchain. Simulation accepts a JSON transaction matching the Sui gRPC transaction schema, or a BCS-encoded transaction with `{"bcs": {"value": "<base64>"}}`.

```graphql
query ($tx: JSON!) {
  simulateTransaction(transaction: $tx, checksEnabled: true, doGasSelection: true) {
    effects {
      status
      gasEffects {
        gasSummary {
          computationCost
          storageCost
        }
      }
    }
    outputs {
      returnValues {
        value {
          json
        }
      }
    }
  }
}
```

Like execution-attached queries, fields selected under simulation are evaluated in a scope immediately after the simulated transaction, without waiting for indexing.

## Migrating to GraphQL from JSON-RPC

The examples in the following sections are tabbed to show both the JSON-RPC call and the equivalent GraphQL structure. For the broader migration, including a decision between gRPC and GraphQL, a JSON-RPC method mapping, and common migration gotchas, see the [JSON-RPC Migration Guide](/develop/accessing-data/json-rpc-migration). For a comprehensive list of all available GraphQL features, consult the [reference](/references/sui-graphql).

#### Example 1: Get total transactions

Get the total number of transactions in the network.

## JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sui_getTotalTransactionBlocks",
  "params": []
}
```

## GraphQL

```graphql
query {
  checkpoint {
    networkTotalTransactions
  }
}
```

#### Example 2: Get a specific transaction

Get the transaction by its digest.

## JSON-RPC

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sui_getTransactionBlock",
  "params": [
    "Hay2tj3GcDYcE3AMHrej5WDsHGPVAYsegcubixLUvXUF",
    {
      "showInput": true,
      "showRawInput": false,
      "showEffects": true,
      "showEvents": true,
      "showObjectChanges": false,
      "showBalanceChanges": false
    }
  ]
}
```

## GraphQL

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

#### Example 3: Get coin objects owned by an address

Return all `Coin<0x2::sui::SUI>` objects that an address owns.

## JSON-RPC

```json
query {
  "jsonrpc": "2.0",
  "id": 1,
  "method": "suix_getCoins",
  "params": [
    "0x5094652429957619e6efa79a404a6714d1126e63f551f4b6c7fb76440f8118c9", //owner
    "0x2::sui::SUI",                                                      //coin type
    "0xe5c651321915b06c81838c2e370109b554a448a78d3a56220f798398dde66eab", //cursor
    3 //limit
  ]
}
```

## GraphQL

```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
      }
    }
  }
}
```

:::info

The cursor is now passed in the `after` (or `before`) fields on the connection, and the limit in the `first` or `last` fields.

:::

There is functionality available to GraphQL that can't be replicated with JSON-RPC, such as:

- Time travel queries with checkpoint `{ query { ... } }`.

- Nested dynamic field look-ups (and paginating dynamic fields with their contents in 1 request).

- Fetching live objects by owner kind and type (such as all shared objects of a certain type).

- Fetching deserialized input and output object contents from a transaction that has just executed.

The following examples demonstrate some of the functions not available with JSON-RPC.

#### Example 4: Getting objects by type

Fetch the latest versions of objects of type `0x2::package::Publisher` that are currently live onchain.

```graphql
query {
  objects(filter: { type: "0x2::package::Publisher" }) {
    nodes {
      address
      digest
      asMoveObject {
        contents { json }
      }
    }
  }
}
```

#### Example 5: Paging through package versions

Find all versions of the Sui framework and list their modules:

```graphql
query {
  packageVersions(address: "0x2") {
    nodes {
      version
      modules {
        nodes {
          name
        }
      }
    }
  }
}
```
