Thanks to visit codestin.com
Credit goes to explorer.arch.network

Arch Network
Docs
Sign in

REST API

The REST API provides indexed views over Arch blocks, transactions, programs, tokens, accounts, and network statistics. This page is generated from the OpenAPI specification shipped with the API server, so it stays in sync with the backend.

Base URL

# Mainnet
https://explorer.arch.network/api/v1/mainnet

# Testnet
https://explorer.arch.network/api/v1/testnet
Choose a network by inserting /mainnet or /testnet after /api/v1 for all chain-data endpoints. Auth/admin endpoints use https://explorer.arch.network/api/v1 (no network segment).

Auth (3)

Authentication and plan discovery.

Blocks (4)

Indexed blocks and block metadata.

Transactions (10)

Transactions and related views.

Accounts (6)

Accounts, balances, and activity.

Tokens (9)

Tokens, holders, and analytics.

Programs (9)

On-chain programs and their activity.

Network (3)

High-level network statistics.

Realtime (1)

Realtime indexing and WebSocket helpers.

Mempool (2)

Pending transactions in the mempool.

Validators (2)

Validators and staking-related data.

Faucet (1)

Request airdrop tokens on testnet or mainnet.

Bitcoin (20)

Bitcoin blockchain data proxied from the Titan indexer (Esplora-compatible). Includes address UTXOs, transaction lookup, fee estimates, broadcast, and chain-tip queries. All endpoints are also available under /api/v1/{network}/bitcoin/... for explicit network selection.

Legacy JSON-RPC compatibility

The JSON-RPC compatibility endpoint is for applications migrating away from direct Arch validator RPC calls. Existing clients can switch their base URL to the indexer while keeping the validator method names and JSON-RPC 2.0 envelope. New read-heavy integrations should prefer the native REST and WebSocket APIs above.

Endpoints

# Mainnet
https://explorer.arch.network/api/v1/mainnet/rpc

# Testnet
https://explorer.arch.network/api/v1/testnet/rpc

# Default network context
https://explorer.arch.network/api/v1/rpc

The endpoint supports JSON-RPC 2.0 single requests, notifications, and batches. It is feature-gated on the API server with RPC_COMPAT__ENABLED=true.

Example request

curl "https://explorer.arch.network/api/v1/testnet/rpc" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"get_block_count","params":[]}'

Discover supported methods

Call rpc.discover to see the methods exposed by the running indexer and whether each method is served from indexed data, proxied to the validator, or answered locally.

curl "https://explorer.arch.network/api/v1/testnet/rpc" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"rpc.discover","params":[]}'

Method sources

  • db - served from the indexer's Postgres copy of chain data.
  • hybrid - served from Postgres first, with validator fallback for indexer lag or schema gaps.
  • proxy - forwarded to the configured upstream validator.
  • local - answered by the API server process.

Supported validator methods

The compatibility surface tracks the production Arch validator RPC module. Methods that the validator does not expose are intentionally not invented here.

Indexed or hybrid reads

  • is_node_ready
  • get_block_count
  • get_block_hash
  • get_best_block_hash
  • get_best_finalized_block_hash
  • get_block / get_block_by_height
  • get_full_block_with_txids
  • read_account_info
  • get_multiple_accounts
  • get_program_accounts (optional indexer paging)
  • get_account_address
  • get_processed_transaction
  • recent_transactions
  • get_transactions_by_block
  • get_transactions_by_ids

Validator proxy calls

  • send_transaction
  • send_transactions
  • request_airdrop
  • create_account_with_faucet
  • get_peers
  • get_current_state
  • get_network_pubkey
  • check_pre_anchor_conflict
  • get_block_execution_report

Local helpers

  • get_version
  • rpc.discover
  • arch_discover

get_program_accounts paging

Validator-shaped calls still work: [program_id, filters] returns a bare array of {pubkey, account}. That unpaged form fails closed above 10,000 matches. For large programs, pass indexer limit (1–1000, default 100) and cursor (exclusive pubkey, hex / base58 / 32-byte array). The result becomes {accounts, next_cursor, has_more, limit}. Walk forward with cursor = next_cursor until has_more is false. Optional data_slice / dataSlice ({offset, length}) trims account.data in SQL. offset paging is rejected; this is keyset-only.

Paged request

curl "https://explorer.arch.network/api/v1/testnet/rpc" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"get_program_accounts","params":{"program_id":"PROGRAM_ID_HEX_OR_BASE58","filters":[{"DataSize":165}],"limit":100,"cursor":null,"data_slice":{"offset":0,"length":32}}}'

Paged result

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "accounts": [{ "pubkey": [/* 32 bytes */], "account": { /* validator AccountInfo */ } }],
    "next_cursor": "aa11...ff00",
    "has_more": true,
    "limit": 100
  }
}

Positional form is also accepted: [program_id, filters, {limit, cursor, data_slice}]. Filters stay the validator / Solana aliases (DataSize / dataSize,DataContent / memcmp).

Migration guidance

  1. Point existing validator JSON-RPC clients at the indexer RPC endpoint.
  2. Run smoke tests for the methods your application uses.
  3. Use rpc.discover to identify which calls are indexed, hybrid, proxied, or local.
  4. Migrate read-heavy paths to native REST endpoints as equivalents exist.
  5. Use WebSockets for live updates instead of polling JSON-RPC reads.
  6. Keep transaction submission on JSON-RPC until a purpose-built native write endpoint is available.

Recommended migration examples: block and transaction reads should move to the REST endpoints in this page; realtime updates should move to WebSockets; validator-local state and write methods can remain on JSON-RPC during the transition.

WebSockets

Subscribe to realtime blocks, transactions, and account updates over a single WebSocket connection.

Endpoints & authentication

The realtime API exposes one WebSocket endpoint per network plus an HTTP lag probe:

  • wss://explorer.arch.network/ws/mainnet – mainnet realtime events.
  • wss://explorer.arch.network/ws/testnet – testnet realtime events.
  • GET https://explorer.arch.network/api/v1/mainnet/realtime/status – Kafka indexer tip lag for mainnet.
  • GET https://explorer.arch.network/api/v1/testnet/realtime/status – Kafka indexer tip lag for testnet.

Each connection only receives events for its network, and every event carries a network field. The legacy /ws endpoint remains available and defaults to testnet.

Deployment configuration

Mainnet and testnet frontend deployments can use the same NEXT_PUBLIC_WS_URL value: wss://explorer.arch.network/ws. The frontend selects the network by connecting to /ws/mainnet or /ws/testnet. Use a different WebSocket host only when a deployment points to a different API environment, such as staging.

Do not use the bare /ws endpoint to select a network; it is retained for compatibility and always resolves to testnet.

Authenticate by passing your API key — the same key used for REST — as the apikey query parameter (api_key is also accepted). The key is checked before the upgrade, so failures arrive as an ordinary HTTP response with a JSON body rather than as a WebSocket message:

# 401 – no apikey / api_key query parameter
{ "error": "missing_api_key", "message": "API key is required as apikey query parameter for WebSocket connections." }

# 401 – key not found
{ "error": "invalid_api_key", "message": "The provided API key is invalid for WebSocket connection." }

# 403 – key revoked, or account not active
{ "error": "key_revoked_or_inactive", "message": "This API key or account is not active." }

# 404 – /ws/:network with an unknown network
{ "error": "invalid_network", "message": "Unknown network; expected 'testnet' or 'mainnet'.", "network": "devnet" }

# 500 – auth lookup failed
{ "error": "auth_backend_error", "message": "Authentication backend error." }

Connection URL

# Mainnet
wss://explorer.arch.network/ws/mainnet?apikey=YOUR_API_KEY

# Testnet
wss://explorer.arch.network/ws/testnet?apikey=YOUR_API_KEY

Client messages

Send JSON text frames with a method field. Two methods are recognized, subscribe and ping, and each returns exactly one reply. Replies carry a status field; events carry topic instead, which is how you tell them apart.

subscribe — narrows the stream to the named topic, accumulating across calls, and echoes the effective topics back. A connection that never subscribes receives every topic for its network, and a subscribe naming no topic widens it back to every topic. An unrecognized topic is rejected and leaves existing subscriptions untouched. There is no unsubscribe; reconnect to drop topics, and close the socket to stop the stream.

{"method": "subscribe", "topic": "block"}
{
  "status": "Subscribed",
  "client_id": "client_id_2f8c1d54-9a1e-4f7d-8f4a-6b0f1d2e3c44",
  "network": "mainnet",
  "topics": ["block"],
  "message": "Successfully subscribed to real-time events"
}

ping — the reply's timestamp is Unix time in seconds, unlike the RFC 3339 timestamp on events. The server never initiates pings, so send this periodically if a proxy between you and the API closes idle connections.

{"method": "ping"}
{"status": "pong", "timestamp": 1735689600}

Any other method is answered with an error naming it, and the connection stays open. Text that is not valid JSON, or JSON without a method field, is dropped with no reply at all.

{"status": "error", "error": "Unknown method: unknown_method"}

Server events

Every event has the shape { "topic": string, "data": object, "timestamp": string, "network": string }, where timestamp is RFC 3339 UTC and records when the API emitted the event rather than when the block was produced. The emitted topics are:

  • block – new blocks, relayed from the validator and then re-emitted enriched with height, transaction_count, and program_counts. Expect more than one message per block.
  • block_activity – partial per-height transaction and program counters, debounced to at most one message every 250 ms per height.
  • transaction – transactions as they are processed.
  • account_update – account state changes.
  • rolledback_transactions – transactions rolled back due to a reorg.
  • reapplied_transactions – transactions re-applied after a reorg.
  • dkg – distributed key generation / validator coordination events.
{
  "topic": "block",
  "data": { "hash": "8f2b...c41d", "height": 543210, "transaction_count": 42 },
  "timestamp": "2025-01-01T00:00:00.412Z",
  "network": "mainnet"
}

See the WebSockets reference for the payload of every topic, including which fields are relayed verbatim from the validator and which are added by the indexer.

Delivery semantics

  • Events are live-only. Nothing is buffered before you connect and nothing is replayed after you reconnect — backfill gaps over REST.
  • Each connection has a 100-event send buffer. A client that stops reading long enough to overflow it is dropped from the broadcast set, and the socket can stay open while silent. Treat an unexpected gap in blocks as a reason to reconnect.
  • Because a topic can repeat for the same block, consumers should be idempotent on data.hash / data.height.

Client example

const apiKey = process.env.ARCH_API_KEY!;
// Use 'wss://explorer.arch.network/ws/testnet' for testnet.
const ws = new WebSocket('wss://explorer.arch.network/ws/mainnet?apikey=' + apiKey);

ws.onopen = () => {
  // Narrows the stream. Without this you receive every topic.
  ws.send(JSON.stringify({ method: 'subscribe', topic: 'block' }));
  ws.send(JSON.stringify({ method: 'subscribe', topic: 'transaction' }));

  // Keeps idle connections alive through intermediate proxies.
  setInterval(() => ws.send(JSON.stringify({ method: 'ping' })), 30_000);
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.status) return; // control reply, not an event
  console.log('[' + msg.topic + ']', msg.data);
};

ws.onclose = () => {
  // No replay on reconnect — backfill any gap over REST.
  console.log('connection closed');
};

Authentication

All non-public Arch Indexer API endpoints require an API key. Keys are tied to an account and plan (Free or Enterprise) and are used for both HTTP and WebSocket access.

Getting an API key

  1. Register a new account via the Developer Portal at /dev or with POST /api/v1/auth/register.
  2. Optionally log in with POST /api/v1/auth/login for backend workflows.
  3. Use the onboarding wizard in the Developer Portal to create your first app.
  4. From the app dashboard, create additional app-scoped keys as needed for different environments (e.g. Production vs Staging).

Example: create an account

POST /api/v1/auth/register
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "a-strong-password"
}

Using API keys with REST

Send your key in either of these headers:

  • Authorization: Bearer <API_KEY> (recommended)
  • X-API-Key: <API_KEY>

curl example

# Mainnet
curl "/api/v1/mainnet/blocks?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Testnet
curl "/api/v1/testnet/blocks?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

JavaScript example (fetch)

// Mainnet
const res = await fetch('/api/v1/mainnet/blocks?limit=10', {
  headers: {
    Authorization: 'Bearer ' + process.env.ARCH_API_KEY,
  },
});
const data = await res.json();
// Testnet
const res = await fetch('/api/v1/testnet/blocks?limit=10', {
  headers: {
    Authorization: 'Bearer ' + process.env.ARCH_API_KEY,
  },
});
const data = await res.json();

Using API keys with WebSockets

WebSocket connections require an API key. Pass it as the apikey query parameter; the same key works for both network-scoped endpoints.

# Mainnet
wss://explorer.arch.network/ws/mainnet?apikey=YOUR_API_KEY

# Testnet
wss://explorer.arch.network/ws/testnet?apikey=YOUR_API_KEY

Apps and app-scoped keys

Each account can own multiple apps. Apps are logical containers for keys and usage – for example, "Wallet backend" or "Analytics dashboard". You create and manage apps and their keys from the Developer Portal; the underlying management APIs are not exposed as part of the public surface area.

Error responses

Authentication errors use a consistent JSON shape:

HTTP 401
{
  "error": "missing_api_key",
  "message": "API key is required. Provide it via the Authorization: Bearer <key> header or X-API-Key header."
}

Rate limits & quotas

The Arch Indexer API enforces per-key rate limits and monthly quotas to ensure reliable service for everyone.

Free plan

  • ~25 requests per second per API key.
  • ~30M requests per month per account (across keys).
  • Reasonable WebSocket usage for realtime subscriptions.

Throttling behavior

When a limit is exceeded, the API responds with HTTP 429:

HTTP 429
{
  "error": "rate_limit_exceeded",
  "message": "Per-second request limit exceeded for this API key.",
  "limit_rps": 25
}

For monthly quota exhaustion:

HTTP 429
{
  "error": "monthly_quota_exceeded",
  "message": "Monthly quota exceeded for this API key.",
  "limit_monthly": 30000000
}

Best practices

  • Implement client-side retries with exponential backoff on 429.
  • Spread traffic across a small number of keys rather than many tiny keys.
  • Use WebSockets for high-frequency realtime updates instead of polling.