REST API Gateway
Structured, indexed Sui blockchain data with powerful filtering, pagination, and search capabilities.
Overview
The REST API provides access to processed and indexed blockchain data with automatic fallback to live RPC calls for comprehensive support.
Base URL: https://api.inodra.com/v1/
Note: Only the endpoints listed in API Reference → REST are implemented in this repo. The examples below are illustrative patterns; adjust to available routes.
Getting Started
1. Basic Setup
javascript
const BASE_URL = 'https://api.inodra.com/v1'
const API_KEY = 'YOUR_API_KEY'
async function apiCall(endpoint) {
const response = await fetch(`${BASE_URL}${endpoint}`, {
headers: {
'x-api-key': API_KEY
}
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return response.json()
}2. Your First Request
javascript
// Get latest checkpoints
const checkpoints = await apiCall('/checkpoints?limit=10')
console.log('Latest checkpoints:', checkpoints.data)
// Get specific checkpoint
const checkpoint = await apiCall('/checkpoints/12345678')
console.log('Checkpoint details:', checkpoint)Common Operations
Get Checkpoints
javascript
// List checkpoints with filtering
const checkpoints = await apiCall('/checkpoints?limit=20&order=desc&from_sequence=12345000')
// Get specific checkpoint
const checkpoint = await apiCall('/checkpoints/12345678')Query Transactions
javascript
// List transactions with filters
const transactions = await apiCall(
`/transactions?${new URLSearchParams({
limit: 50,
sender: '0x123...',
from_checkpoint: 12345000,
to_checkpoint: 12345100
})}`
)
// Get specific transaction with full details
const transaction = await apiCall(
'/transactions/ABC123...?expand=effects,events,object_changes,balance_changes'
)Address Operations
javascript
// Get address balance
const balance = await apiCall('/addresses/0x123.../balance')
// Get address balance for specific coin
const suiBalance = await apiCall('/addresses/0x123.../balance?coin_type=0x2::sui::SUI')
// Get objects owned by address
const objects = await apiCall('/addresses/0x123.../objects?limit=50&type=0x2::coin::Coin')
// Get address transaction history
const txHistory = await apiCall('/addresses/0x123.../transactions?limit=100&role=sender')Object Queries
javascript
// Search objects by owner and type
const objects = await apiCall(
`/objects?${new URLSearchParams({
owner: '0x123...',
type: '0x2::coin::Coin<0x2::sui::SUI>',
limit: 50
})}`
)
// Get specific object details
const object = await apiCall('/objects/0x456...?expand=content,display,owner')Event Queries
javascript
// Query events by type
const events = await apiCall(
`/events?${new URLSearchParams({
package_id: '0x2',
module: 'coin',
event_type: 'Transfer',
limit: 100,
from_checkpoint: 12345000
})}`
)
// Query events by sender
const senderEvents = await apiCall('/events?sender=0x123...&limit=50')Coin Information
javascript
// Get coin metadata
const coinMeta = await apiCall('/coins/0x2::sui::SUI/metadata')
// List all coins by creator
const coins = await apiCall('/coins?creator=0x2&limit=50')Advanced Filtering
Time-based Filtering
javascript
// Transactions in specific time range
const recentTx = await apiCall(
`/transactions?${new URLSearchParams({
from_timestamp: Date.now() - 24 * 60 * 60 * 1000, // 24 hours ago
to_timestamp: Date.now(),
limit: 100
})}`
)Complex Object Queries
javascript
// NFTs owned by specific address
const nfts = await apiCall(
`/objects?${new URLSearchParams({
owner: '0x123...',
type: '0x2::nft::NFT',
limit: 100
})}`
)
// Coins of specific type with minimum balance
const largeCoinObjects = await apiCall(
`/objects?${new URLSearchParams({
type: '0x2::coin::Coin<0x2::sui::SUI>',
min_balance: '1000000000', // 1 SUI
limit: 50
})}`
)Event Filtering
javascript
// Events from specific package and module
const moduleEvents = await apiCall(
`/events?${new URLSearchParams({
package_id: '0x2',
module: 'coin',
from_checkpoint: 12000000,
limit: 200
})}`
)
// Events affecting specific address
const addressEvents = await apiCall(
`/events?${new URLSearchParams({
affected_address: '0x123...',
event_type: 'Transfer',
limit: 100
})}`
)Pagination Patterns
Offset-based Pagination
javascript
async function getAllTransactions(batchSize = 100) {
let allTransactions = []
let offset = 0
let hasMore = true
while (hasMore) {
const response = await apiCall(`/transactions?limit=${batchSize}&offset=${offset}`)
allTransactions.push(...response.data)
hasMore = response.pagination.hasNextPage
offset += batchSize
}
return allTransactions
}Cursor-based Pagination
javascript
async function getAllEvents(eventType) {
let allEvents = []
let cursor = null
let hasMore = true
while (hasMore) {
const params = new URLSearchParams({
event_type: eventType,
limit: 100
})
if (cursor) {
params.append('cursor', cursor)
}
const response = await apiCall(`/events?${params}`)
allEvents.push(...response.data)
hasMore = response.pagination.hasNextPage
cursor = response.pagination.cursor
}
return allEvents
}Error Handling
javascript
async function safeApiCall(endpoint, retries = 3) {
for (let i = 0; i <= retries; i++) {
try {
const response = await fetch(`${BASE_URL}${endpoint}`, {
headers: {
'x-api-key': API_KEY
}
})
// Handle backoff for 429 responses
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('retry-after') || '60')
if (i < retries) {
await sleep(retryAfter * 1000)
continue
}
}
if (!response.ok) {
const error = await response.json()
throw new Error(`API Error: ${error.error.message}`)
}
const data = await response.json()
// Optional: log response metadata
return data
} catch (error) {
if (i === retries) throw error
// Exponential backoff
await sleep(Math.pow(2, i) * 1000)
}
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}Real-time Updates with Polling
javascript
class RealTimeMonitor {
constructor(apiKey) {
this.apiKey = apiKey
this.lastCheckpoint = null
this.subscribers = []
}
subscribe(callback) {
this.subscribers.push(callback)
}
async start(interval = 5000) {
this.intervalId = setInterval(async () => {
try {
const response = await apiCall('/checkpoints?limit=1')
const latestCheckpoint = response.data[0]
if (
!this.lastCheckpoint ||
latestCheckpoint.sequenceNumber > this.lastCheckpoint.sequenceNumber
) {
this.lastCheckpoint = latestCheckpoint
// Notify subscribers
this.subscribers.forEach((callback) => {
callback('checkpoint', latestCheckpoint)
})
}
} catch (error) {
console.error('Polling error:', error)
}
}, interval)
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId)
}
}
}
// Usage
const monitor = new RealTimeMonitor(API_KEY)
monitor.subscribe((type, data) => {
console.log('New checkpoint:', data.sequenceNumber)
})
monitor.start()Response Caching
javascript
class CachedAPIClient {
constructor(apiKey, defaultTTL = 60000) {
this.apiKey = apiKey
this.cache = new Map()
this.defaultTTL = defaultTTL
}
async get(endpoint, ttl = this.defaultTTL) {
const cacheKey = endpoint
const cached = this.cache.get(cacheKey)
if (cached && Date.now() - cached.timestamp < ttl) {
console.log('Cache hit:', endpoint)
return cached.data
}
console.log('Cache miss:', endpoint)
const data = await apiCall(endpoint)
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
})
return data
}
clearCache() {
this.cache.clear()
}
}
// Usage
const client = new CachedAPIClient(API_KEY)
// This will make an API call
const checkpoints1 = await client.get('/checkpoints?limit=10')
// This will use cached data (within 60 seconds)
const checkpoints2 = await client.get('/checkpoints?limit=10')OpenAPI Integration
javascript
// Using OpenAPI spec for type generation
import { DefaultApi, Configuration } from './generated/api'
const config = new Configuration({
basePath: 'https://api.inodra.com/v1',
apiKey: 'YOUR_API_KEY'
})
const api = new DefaultApi(config)
// Type-safe API calls
const checkpoints = await api.getCheckpoints({ limit: 10, order: 'desc' })
const transaction = await api.getTransaction({
digest: 'ABC123...',
expand: 'effects,events'
})Monitoring and Analytics
javascript
class APIUsageTracker {
constructor() {
this.requests = []
this.startTime = Date.now()
}
logRequest(endpoint, responseTime, status) {
this.requests.push({
endpoint,
responseTime,
status,
timestamp: Date.now()
})
}
getStats() {
const now = Date.now()
const recentRequests = this.requests.filter(
(req) => now - req.timestamp < 60000 // Last minute
)
const avgResponseTime =
recentRequests.reduce((sum, req) => sum + req.responseTime, 0) / recentRequests.length
const errorRate =
recentRequests.filter((req) => req.status >= 400).length / recentRequests.length
return {
totalRequests: this.requests.length,
recentRequestsPerMinute: recentRequests.length,
avgResponseTime: avgResponseTime || 0,
errorRate: errorRate || 0
}
}
}
// Wrap API calls with tracking
const tracker = new APIUsageTracker()
async function trackedApiCall(endpoint) {
const startTime = Date.now()
try {
const result = await apiCall(endpoint)
const responseTime = Date.now() - startTime
tracker.logRequest(endpoint, responseTime, 200)
return result
} catch (error) {
const responseTime = Date.now() - startTime
tracker.logRequest(endpoint, responseTime, error.status || 500)
throw error
}
}Advantages of REST Gateway
📊 Structured Data
- Pre-processed and indexed blockchain data
- Optimized for filtering and analytics
- Fast response times for complex queries
🔍 Advanced Search
- Multi-field filtering and sorting
- Full-text search capabilities
- Aggregated statistics and metrics
📈 Analytics Ready
- Historical data analysis
- Trend identification
- Business intelligence integration
🌐 Web-friendly
- Standard HTTP methods and status codes
- JSON responses
- Easy integration with web frameworks
When to Use REST Gateway
Perfect for:
- Web applications and dashboards
- Analytics and reporting systems
- Mobile applications
- Integration with existing REST-based systems
- Data analysis and business intelligence
Interactive Documentation
Explore the complete REST API at:
- Interactive Docs - Try endpoints live
- OpenAPI Spec - Generate client libraries
Next Steps
- Explore the API Reference for all available endpoints
- Try the interactive API documentation
- Download the OpenAPI specification
- Join our Discord for REST API tips
Ready to analyze blockchain data? Start querying! 🚀