GraphQL for Sui RPC (Beta)
This content describes an alpha/beta feature or service. These early stage features and services are in active development, so details are likely to change.
This feature or service is currently available in
- Devnet
- Testnet
- Mainnet
Important concepts when working with GraphQL include altering behavior using HTTP headers, re-using query snippets with variables and fragments, consuming paginated queries, and understanding and working within the limits enforced by the service.
For more details on GraphQL fundamentals, see the introductory documentation from GraphQL and GitHub.
Refer to Access Sui Data for an overview of options to access Sui network data.
The GraphQL RPC release stage is currently in beta. Refer to the high-level timeline for releases.
Switch any apps that still use the GraphQL Alpha endpoints (https://sui-mainnet.mystenlabs.com/graphql, https://sui-testnet.mystenlabs.com/graphql, and https://sui-devnet.mystenlabs.com/graphql) to the GraphQL Beta endpoints as soon as possible to avoid disruption of service.
-
GraphQL Beta Mainnet:
https://graphql.mainnet.sui.io/graphql -
GraphQL Beta Testnet:
https://graphql.testnet.sui.io/graphql -
GraphQL Beta Devnet:
https://graphql.devnet.sui.io/graphql
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.
$ 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
HTTP/2 200
content-type: application/json
content-length: 179
x-sui-rpc-request-id: f5442058-47ab-4360-8295-61c009f38516
x-sui-rpc-version: 1.56.1-
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.
In the following example, a variable supplies the ID of the epoch being queried:
query ($epochID: Int) {
epoch(id: $epochID) {
referenceGasPrice
}
}
Variables:
{
"epochID": 100
}
Within the GraphQL IDE
When using the GraphQL online integrated development environment (IDE), 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:
$ 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. The following example uses fragments to factor out a reusable snippet representing a Move value:
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:
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 on the maximum page size for variable size responses (like the transactionBlock query) and you must fetch further results through 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, meaning its name ends in Connection, and it contains at minimum the following fields:
-
pageInfoof 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 (TransactionBlockin the previous example). -
edges: Similar tonodesbut associating each node with its cursor.
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. If a paginated query reads the state of the network at a specific checkpoint (checkpoint X), the query returns cursors that can be used by subsequent requests. When a future call fetches the next page of results, it uses the previous query's cursors and continues reading from the network as it existed at checkpoint X, even if newer checkpoints are available. Currently, the available range offers roughly 1 hour to finish pagination.
This property requires that cursors that are used together (such as when supplying an after and before bound) are fixed on the same checkpoint, otherwise the query produces an error.
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, 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: 3would selectR0,R1,R2. -
A limit of
last: 3would selectR7,R8,R9.
It is an error to apply both a first and a last limit.
Examples
To see these principles put into practice, consult the examples for paginating forwards and paginating backwards.
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:
{
serviceConfig {
maxQueryDepth
maxQueryNodes
maxOutputNodes
defaultPageSize
maxPageSize
queryTimeoutMs
maxQueryPayloadSize
maxTypeArgumentDepth
maxTypeArgumentWidth
maxTypeNodes
maxMoveValueDepth
}
}
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 may 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:
| 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) |
Retention is dependent on the pruning policies of datasource pipelines configured by the stack operator. For production configuration examples, see Indexer Stack Setup.
Querying available range
Query the available checkpoint range using serviceConfig.availableRange:
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"]forTransactionFilter,["module"]forEventFilter, or direct query parameter names like["version"].
Match your query parameters to filter names: if your query uses filter: { affectedAddress: "0x..." }, pass filters: ["affectedAddress"].
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
# Check available range for transaction queries filtered by affected address
{
serviceConfig {
availableRange(type: "Query", field: "transactions", filters: ["affectedAddress"]) {
first {
sequenceNumber
}
last {
sequenceNumber
}
}
}
}
# Check available range for a checkpoint transactions queries filtered by affected object
{
serviceConfig {
availableRange(type: "Checkpoint", field: "Transactions", filters: ["affectedObject"]) {
first {
sequenceNumber
}
last {
sequenceNumber
}
}
}
}
Each field in a nested query has its own retention limit:
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:
- Check the available range first using
serviceConfig.availableRangebefore starting long pagination runs. - Use fresh cursors. Old cursors may reference checkpoints that have since been pruned.
- Adjust your query bounds. Ensure
afterCheckpoint/beforeCheckpointfilters fall within the available range.
Related links
Introductory guide to making queries of the Sui RPC using the GraphQL service.
GraphQL is a public service for the Sui RPC that enables you to efficiently interact with the Sui network.
The GraphQL RPC Beta service offers a structured way for your clients to interact with data on the Sui blockchain. It accesses data processed by a general-purpose indexer and can connect to an archival store for historical network state.