gRPC Gateway
Sui gRPC with full chain history - live node and archival storage behind a single endpoint, with server reflection enabled.
What is gRPC?
gRPC is a high-performance RPC framework that uses Protocol Buffers for serialization and HTTP/2 for transport. Unlike JSON-RPC which sends human-readable text, gRPC sends compact binary data - making it faster and more bandwidth-efficient.
| Feature | gRPC | JSON-RPC |
|---|---|---|
| Data format | Binary (Protocol Buffers) | JSON text |
| Transport | HTTP/2 | HTTP/1.1 |
| Streaming | Native support | Not supported |
| Type safety | Compile-time | Runtime |
| Code generation | Built-in | Manual |
| Typical latency | Lower | Higher |
For a deeper dive, see the official gRPC documentation.
Why Sui is Moving to gRPC
📌 Important: JSON-RPC Deprecation
Sui has deprecated JSON-RPC in favor of gRPC and GraphQL. If you're currently using JSON-RPC, see our migration guide.
Sui chose gRPC for:
- Performance: Binary serialization is faster than JSON
- Streaming: Subscribe to real-time checkpoint updates
- Type safety: Catch errors at compile time, not runtime
- Modern tooling: Better developer experience with generated clients
Inodra makes gRPC easy - just add your API key and start making calls.
One Endpoint: Live Node + Archival Storage
Endpoint: mainnet-grpc.inodra.com:443 (TLS required)
Upstream, Sui splits gRPC access across separate endpoints: a full node for recent state (older data is pruned) and a dedicated archival service that only implements a subset of the API. With Inodra, both live behind one endpoint:
- Live node - current state, transaction execution, simulation, and real-time checkpoint streaming.
- Archival storage - every checkpoint, transaction, and object version back to genesis.
The gateway routes each request to the right backend automatically. Same endpoint, same API key, same services - whether you're asking for the latest checkpoint or checkpoint 1,000 from 2023:
# Latest checkpoint and full-history checkpoint - identical call, same endpoint
grpcurl -H "x-api-key: YOUR_API_KEY" -d '{}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.LedgerService/GetServiceInfo
grpcurl -H "x-api-key: YOUR_API_KEY" -d '{"sequence_number": 1000, "read_mask": {"paths": ["summary", "contents"]}}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.LedgerService/GetCheckpointKey Features:
- Full Sui gRPC compatibility, all services on one endpoint
- Complete chain history (no separate archival endpoint or pruning horizon)
- Server reflection enabled (explore without proto files)
- HTTP/2 for optimal performance
- TLS encryption
Explicit Routing: x-inodra-route
By default the gateway routes automatically: requests go to the live node first, and fall back to archival storage when the node no longer has the data (or is briefly unavailable). Checkpoint numbers below the node's retention floor skip straight to archival, and explicit epoch lookups are served archival-first so historical epochs return complete protocol configs.
If you want to pin a request to one backend, set the x-inodra-route header:
| Value | Behavior |
|---|---|
node | Live node only - pruned history returns NOT_FOUND |
archival | Archival storage only - lags the live tip by a few seconds |
| (unset) | Automatic routing with fallback (recommended) |
# Force the archival backend
grpcurl -H "x-api-key: YOUR_API_KEY" -H "x-inodra-route: archival" \
-d '{"sequence_number": 1000, "read_mask": {"paths": ["summary"]}}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.LedgerService/GetCheckpoint
# Force the live node (recent data only)
grpcurl -H "x-api-key: YOUR_API_KEY" -H "x-inodra-route: node" \
-d '{"digest": "TRANSACTION_DIGEST_HERE"}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.LedgerService/GetTransactionWhat to know before pinning:
- Explicit routing disables fallback. The gateway calls exactly the backend you named and returns its answer as-is - including
NOT_FOUNDfor data the other backend has. - The header applies to LedgerService methods only. The other services (state, execution, streaming, packages, names, signature verification) are live-only and always served by the node;
GetServiceInfoalways reflects the node. - Batch methods (
BatchGetTransactions,BatchGetObjects) normally split per item across both backends; with an explicit route the whole batch goes to one backend.
gRPC vs gRPC-Web
Inodra supports both native gRPC and gRPC-Web on the same endpoint:
| Protocol | Transport | Use Case | Streaming |
|---|---|---|---|
| gRPC-Web | GrpcWebFetchTransport | Browsers, universal | Server-side only |
| Native gRPC | GrpcTransport | Node.js, servers, CLI | Full (bidirectional) |
Why two protocols?
- Browsers can't make native gRPC calls (HTTP/2 limitations)
- gRPC-Web wraps gRPC in a browser-compatible format
- Inodra's infrastructure automatically handles both
Which should you use?
- Browser/Frontend: gRPC-Web (
GrpcWebFetchTransport) - Node.js/Backend: Either works; native gRPC has full streaming support
- Universal code: gRPC-Web (works everywhere)
Note: gRPC-Web uses the standard binary protocol (
application/grpc-web+proto), which is what all gRPC-Web client libraries send. JSON payloads over gRPC-Web are not part of the Sui protocol stack and are not supported - use the GraphQL API or JSON-RPC gateway if you want JSON over plain HTTP.
Server Reflection: Explore Without Proto Files
One of the biggest barriers to getting started with gRPC is downloading and compiling proto files. With Inodra, you don't need to - server reflection lets you discover services, methods, and message types directly from the API.
Think of it as a self-documenting API. You can:
- List all available services
- See what methods each service offers
- Inspect request/response message formats
- Make calls without any local proto files
This is perfect for prototyping, debugging, or learning the API before setting up a full development environment.
Quick Start with grpcurl
grpcurl is the easiest way to explore gRPC APIs:
# macOS
brew install grpcurl
# Linux
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# Windows (via Scoop)
scoop install grpcurlDiscover Available Services
# List all services (no proto files needed!)
grpcurl -H "x-api-key: YOUR_API_KEY" \
mainnet-grpc.inodra.com:443 list
# Example output:
# grpc.reflection.v1.ServerReflection
# sui.rpc.v2.LedgerService
# sui.rpc.v2.MovePackageService
# sui.rpc.v2.NameService
# sui.rpc.v2.SignatureVerificationService
# sui.rpc.v2.StateService
# sui.rpc.v2.SubscriptionService
# sui.rpc.v2.TransactionExecutionServiceExplore a Service
# See all methods in LedgerService
grpcurl -H "x-api-key: YOUR_API_KEY" \
mainnet-grpc.inodra.com:443 \
describe sui.rpc.v2.LedgerService
# See the request format for a specific method
grpcurl -H "x-api-key: YOUR_API_KEY" \
mainnet-grpc.inodra.com:443 \
describe sui.rpc.v2.GetCheckpointRequestMake API Calls
# Get current epoch info (includes gas price, protocol version)
grpcurl -H "x-api-key: YOUR_API_KEY" \
-d '{}' \
mainnet-grpc.inodra.com:443 \
sui.rpc.v2.LedgerService/GetEpoch
# Get specific checkpoint - works for any checkpoint since genesis
grpcurl -H "x-api-key: YOUR_API_KEY" \
-d '{"sequence_number": 12345678}' \
mainnet-grpc.inodra.com:443 \
sui.rpc.v2.LedgerService/GetCheckpoint
# Get transaction by digest - full history, no pruning
grpcurl -H "x-api-key: YOUR_API_KEY" \
-d '{"digest": "TRANSACTION_DIGEST_HERE"}' \
mainnet-grpc.inodra.com:443 \
sui.rpc.v2.LedgerService/GetTransaction🚀 Ready to try it?
Get your free API key and start exploring with grpcurl in under 2 minutes.
Available Services
Sui's gRPC API is organized into specialized services. All of them are available on the one endpoint, with full-history coverage:
| Service | Purpose | Common Use Cases |
|---|---|---|
| LedgerService | Query blockchain data (full history) | Indexers, block explorers, archives |
| StateService | Query current on-chain state | Wallets, DeFi apps, balance checks |
| TransactionExecutionService | Submit and simulate transactions | Any dApp that writes to Sui |
| SubscriptionService | Stream real-time checkpoint data | Live dashboards, monitoring |
| MovePackageService | Access Move package metadata | Developer tools, contract analysis |
| SignatureVerificationService | Validate signatures | Authentication, zkLogin |
| NameService | Resolve SuiNS domain names | Name resolution, reverse lookups |
For detailed method documentation, see Sui's gRPC API reference.
Field Masks
Most read methods accept a read_mask to select exactly the fields you need. This reduces response size, latency, and streaming costs:
# Only the sequence number, digest, and summary of a checkpoint
grpcurl -H "x-api-key: YOUR_API_KEY" \
-d '{"sequence_number": 12345678, "read_mask": {"paths": ["sequence_number", "digest", "summary"]}}' \
mainnet-grpc.inodra.com:443 \
sui.rpc.v2.LedgerService/GetCheckpointNote: with grpcurl, write the field mask as a JSON object (
{"paths": [...]}). The compact string form ("read_mask": "summary,contents") is rejected by current grpcurl versions. Generated clients (TypeScript, Go, etc.) take aFieldMaskwithpathseither way.One archival caveat: for historical checkpoints,
GetCheckpointservessummary,contents,signature, and BCS masks - but the per-transactiontransactions.*masks are only populated for recent checkpoints (the same is true of Sui's own archival service). To read full transaction data at any age, take the digests fromcontentsand callGetTransaction/BatchGetTransactions, which return every field across the entire history.
TypeScript Quick Start
Option 1: Native gRPC (Recommended)
For server-side applications with full bidirectional streaming support. This is the recommended approach for backends:
import { SuiGrpcClient } from '@mysten/sui/grpc'
import { GrpcTransport } from '@protobuf-ts/grpc-transport'
import { ChannelCredentials } from '@grpc/grpc-js'
const transport = new GrpcTransport({
host: 'mainnet-grpc.inodra.com:443',
channelCredentials: ChannelCredentials.createSsl(),
meta: { 'x-api-key': 'YOUR_API_KEY' }
})
const client = new SuiGrpcClient({ network: 'mainnet', transport })
// Get current epoch info (includes gas price, protocol version)
const { response } = await client.ledgerService.getEpoch({})
console.log('Epoch:', response.epoch?.epoch, 'Gas price:', response.epoch?.referenceGasPrice)
// Get account balance (high-level method)
const balance = await client.getBalance({
owner: '0xabc...',
coinType: '0x2::sui::SUI'
})
console.log('Balance:', balance.balance?.balance)
// Get an object
const object = await client.getObject({ objectId: '0x123...' })Required packages:
npm install @mysten/sui @protobuf-ts/grpc-transport @grpc/grpc-jsNote: SDK 2.0 provides high-level methods (e.g.,
client.getBalance()) and low-level service methods (e.g.,client.ledgerService.getEpoch()). High-level methods return processed data; service methods return{ response, status, headers }.
Option 2: gRPC-Web (Browsers)
For browser applications where native gRPC isn't available:
import { SuiGrpcClient } from '@mysten/sui/grpc'
import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport'
const transport = new GrpcWebFetchTransport({
baseUrl: 'https://mainnet-grpc.inodra.com',
meta: { 'x-api-key': 'YOUR_API_KEY' }
})
const client = new SuiGrpcClient({ network: 'mainnet', transport })
// Get account balance
const balance = await client.getBalance({
owner: '0xabc...',
coinType: '0x2::sui::SUI'
})
console.log('Balance:', balance.balance?.balance)Required packages:
npm install @mysten/sui @protobuf-ts/grpcweb-transportOption 3: Dynamic Loading (No Proto Compilation)
For more control or when the SDK doesn't cover your use case:
import * as grpc from '@grpc/grpc-js'
import * as protoLoader from '@grpc/proto-loader'
// Download proto files first:
// git clone https://github.com/MystenLabs/sui-apis.git
const packageDefinition = protoLoader.loadSync('sui/rpc/v2/ledger_service.proto', {
includeDirs: ['./sui-apis/proto'],
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
})
const loaded = grpc.loadPackageDefinition(packageDefinition) as any
// Create client with TLS (package path is sui.rpc.v2)
const client = new loaded.sui.rpc.v2.LedgerService(
'mainnet-grpc.inodra.com:443',
grpc.credentials.createSsl()
)
// Add API key to metadata
const metadata = new grpc.Metadata()
metadata.add('x-api-key', 'YOUR_API_KEY')
// Get current epoch info (keepCase preserves snake_case field names)
client.GetEpoch({}, metadata, (err: Error | null, response: any) => {
if (err) {
console.error('Error:', err.message)
return
}
console.log('Epoch:', response.epoch?.epoch, 'Gas price:', response.epoch?.reference_gas_price)
})Protocol Buffer Definitions
For code generation and type definitions, Sui provides official .proto files:
- Sui gRPC Proto Files - Official protocol buffer definitions from Sui's GitHub
- Sui gRPC Documentation - Complete gRPC API reference and method documentation
Since Inodra serves Sui's standard gRPC API, you can use Sui's official proto files directly for client generation.
Using Proto Files
# Clone Sui's proto definitions
git clone https://github.com/MystenLabs/sui-apis.git
cd sui-apis/proto
# Node.js uses dynamic loading via @grpc/proto-loader, no code generation neededIn Node.js, load the cloned protos directly with @grpc/proto-loader - see Option 3 above for a complete working example. For other languages (Go, Python, Rust, etc.), generate clients from the same proto files with protoc or buf and point them at mainnet-grpc.inodra.com:443 with your API key in the request metadata.
Streaming
gRPC's real power is streaming - subscribe to real-time updates without polling:
// Subscribe to live checkpoint updates (optionally pass a read_mask to limit fields)
const call = client.subscriptionService.subscribeCheckpoints({})
for await (const message of call.responses) {
console.log('New checkpoint:', message.checkpoint?.sequenceNumber)
// Process each checkpoint as it arrives - no polling delay
}Note: the stream starts at the live tip -
SubscribeCheckpointsRequesthas no start-cursor field. To backfill history before going live, page throughLedgerService.GetCheckpointup to the first streamedcursor, then continue from the stream.
Why streaming matters:
- Lower latency: Data arrives as soon as it's available
- Less server load: One connection vs repeated polling
- Cost efficient: Standard gRPC calls cost 1 Credit each
Streaming billing (SubscribeCheckpoints):
Checkpoint streaming is billed per data volume to reflect the bandwidth used:
| Metric | Rate |
|---|---|
| Per MB streamed | 20 Credits |
| Minimum per message | 1 Credit |
Using field masks to request only the data you need (e.g., events only) significantly reduces costs. A full checkpoint averages ~950 KB (~19 Credits), while a masked request for events only is much smaller.
Error Handling
gRPC uses standard status codes for errors. With SuiGrpcClient, errors are thrown as exceptions:
import { RpcError } from '@protobuf-ts/runtime-rpc'
try {
const object = await client.getObject({ objectId: '0x123...' })
} catch (err) {
if (err instanceof RpcError) {
switch (err.code) {
case 'UNAUTHENTICATED':
console.error('Invalid API key')
break
case 'NOT_FOUND':
console.error('Object not found')
break
case 'UNAVAILABLE':
console.error('Service unavailable, retrying...')
break
case 'RESOURCE_EXHAUSTED':
console.error('Rate limited')
break
default:
console.error('Error:', err.message)
}
}
}Note: status codes are canonical and safe to branch on. Error message strings are intentionally generic (e.g.,
Resource not found) - build retry and fallback logic on the status code, not the message text.
When to Use gRPC
Perfect for:
- High-frequency trading applications
- Indexers and analytics over the full chain history
- Real-time monitoring dashboards
- Microservices that need type safety
- Mobile apps (bandwidth-sensitive)
- Applications requiring sub-100ms latency
- Any use case needing streaming
Consider alternatives if:
- You want flexible JSON queries over HTTP - see the GraphQL API
- Your team isn't familiar with Protocol Buffers
Next Steps
- Migrate from JSON-RPC if you're transitioning from the deprecated API
- Download Sui's proto files for code generation
- Monitor usage in the Inodra dashboard
- Join our Discord for gRPC best practices