API Reference

Base URL: https://rpc.nodius.xyz

All authenticated endpoints require one of: per-request wallet signature headers (X-Pubkey, X-Signature, X-Timestamp, X-Nonce), X-Api-Key header, or Authorization: Bearer <token>. See Authentication for signing details.

Signing message format: solana-nodius:v2:{METHOD}:{PATH}:{TIMESTAMP}:{NONCE}:{SHA256_BODY_HEX} โ€” signed with Ed25519, signature is base58-encoded. Body hash is SHA-256 hex of the raw request body bytes (empty string for GET).


RPC Endpoint

POST / (or /rpc)

The primary Solana RPC endpoint. Send JSON-RPC 2.0 requests for all Solana methods. Also available at POST /rpc.

Request body:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getSlot",
  "params": []
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 285943216
}

All responses from authenticated RPC endpoints include X-Credits-Remaining and X-Request-Id headers. Rate-limit headers (X-Ratelimit-Limit, X-Ratelimit-Remaining, X-Ratelimit-Reset) are included on all authenticated responses. On 429 responses, Retry-After is also present. Public endpoints (/health, /pricing, /capabilities) do not include credit or rate-limit headers.

import { NodiusClient } from "@nodius/sdk";

const rpc = NodiusClient.fromSecretKey(
  "https://rpc.nodius.xyz",
  "your_base58_secret_key_here"
);

const slot = await rpc.call("getSlot");
console.log("Current slot:", slot);

const balance = await rpc.call("getBalance", [
  "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg",
]);
console.log("Balance:", balance.value);

const accountInfo = await rpc.call("getAccountInfo", [
  "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
  { encoding: "base64" },
]);

Use rpc.getAccountInfo(pubkey) as a typed wrapper for the Solana RPC method, or rpc.getBillingAccount() for your Nodius billing account (balance, deposit address, usage stats).

from nodius import NodiusClient

rpc = NodiusClient.from_base58(
    "https://rpc.nodius.xyz",
    secret_key="your_base58_secret_key_here",
)

slot = rpc.call("getSlot")
print("Current slot:", slot)

balance = rpc.call("getBalance", ["vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg"])
print("Balance:", balance)

account_info = rpc.call("getAccountInfo", [
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    {"encoding": "base64"},
])

Use rpc.get_account_info(pubkey) as a typed wrapper for the Solana RPC method, or rpc.get_billing_account() for your Nodius billing account (balance, deposit address, usage stats).

# See authentication.md for the full wallet signing algorithm.
# For API key auth:
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "getSlot"}).encode()

req = urllib.request.Request(
    "https://rpc.nodius.xyz",
    data=body,
    headers={"Content-Type": "application/json", "X-Api-Key": API_KEY},
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    print("Credits:", resp.headers.get("X-Credits-Remaining"))
    print("Slot:", json.loads(resp.read())["result"])
// See authentication.md for wallet signing. For API key auth:
const API_KEY = "srpc_live_your_api_key_here";
const res = await fetch("https://rpc.nodius.xyz", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": API_KEY,
  },
  body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getSlot" }),
});
const result = await res.json();
console.log("Slot:", result.result);
curl -X POST https://rpc.nodius.xyz \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"jsonrpc":"2.0","id":1,"method":"getSlot"}'

Batch Requests

Send an array of JSON-RPC requests (max 20 per batch). Each method is billed individually.

[
  {"jsonrpc": "2.0", "id": 1, "method": "getSlot"},
  {"jsonrpc": "2.0", "id": 2, "method": "getBlockHeight"},
  {"jsonrpc": "2.0", "id": 3, "method": "getBalance", "params": ["vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg"]}
]
const responses = await rpc.callBatch([
  { method: "getSlot" },
  { method: "getBlockHeight" },
]);
results = rpc.call_batch([
    {"method": "getSlot"},
    {"method": "getBlockHeight"},
])
body = json.dumps([
    {"jsonrpc": "2.0", "id": 1, "method": "getSlot"},
    {"jsonrpc": "2.0", "id": 2, "method": "getBlockHeight"},
]).encode()
# POST to https://rpc.nodius.xyz with X-Api-Key header
const body = JSON.stringify([
  { jsonrpc: "2.0", id: 1, method: "getSlot" },
  { jsonrpc: "2.0", id: 2, method: "getBlockHeight" },
]);
// POST to https://rpc.nodius.xyz with X-Api-Key header
curl -X POST https://rpc.nodius.xyz \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '[{"jsonrpc":"2.0","id":1,"method":"getSlot"},{"jsonrpc":"2.0","id":2,"method":"getBlockHeight"}]'

Supported Methods & Credits

Category Credits Methods
READ_CHEAP 1 getAccountInfo, getBalance, getBlockCommitment, getBlockHeight, getBlockProduction, getBlockTime, getClusterNodes, getEpochInfo, getEpochSchedule, getFeeForMessage, getFirstAvailableBlock, getGenesisHash, getHealth, getHighestSnapshotSlot, getIdentity, getInflationGovernor, getInflationRate, getInflationReward, getLatestBlockhash, getLeaderSchedule, getMaxRetransmitSlot, getMaxShredInsertSlot, getMinimumBalanceForRentExemption, getMultipleAccounts, getRecentPerformanceSamples, getRecentPrioritizationFees, getSignatureStatuses, getSlot, getSlotLeader, getSlotLeaders, getStakeActivation, getStakeMinimumDelegation, getTokenAccountBalance, getTokenLargestAccounts, getTokenSupply, getTransactionCount, getVersion, getVoteAccounts, isBlockhashValid, minimumLedgerSlot, getPriorityFeeEstimate, suggestPriorityFee
READ_MEDIUM 5 simulateTransaction, getAssetsByOwner, getEnrichedTransactionโ€ , explainTransactionโ€ , getTransaction, getSignaturesForAddress
READ_HEAVY 25 simulateBundle, getBlock, getBlocks, getBlocksWithLimit, getTokenAccountsByOwner, getTokenAccountsByDelegate โ€” getBlock methods served from hot-node retention (โ‰ˆ3.7h of history). Slots outside retention return Agave's native "cleaned up" error.
SEND 2 sendTransaction, sendSmartTransaction
SEND_BUNDLE 10 sendBundle
ARCHIVE_HISTORY 5 getConfirmedTransaction, getConfirmedSignaturesForAddress2 โ€” profile-gated, returns -32004 if disabled
DENIED โ€” requestAirdrop, getProgramAccounts, getSupply

โ€  getEnrichedTransaction and explainTransaction are archive-gated (return -32004 when the archive profile is disabled). Other READ_MEDIUM methods are served by the hot node.

Error Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}

See Error Reference for all error codes.


Account Management

POST /account/api-key

Generate an API key. Requires wallet signature auth. If no account record exists for your pubkey, it is created automatically with a zero balance โ€” so this endpoint works before any deposit.

Response:

{
  "ok": true,
  "api_key": "srpc_live_<base64url>",
  "message": "Save this key โ€” it will not be shown again."
}

The API key is returned only once. Store it securely. Bots can skip this entirely and use per-request wallet signatures.

const result = await rpc.generateApiKey();
console.log("API key:", result.api_key);
// SDK auto-stores the key for subsequent calls
result = rpc.generate_api_key()
print("API key:", result["api_key"])
# SDK auto-stores the key for subsequent calls
# Requires wallet signature auth โ€” see authentication.md
# PATH = "/account/api-key", body = "{}"
// Requires wallet signature auth โ€” see authentication.md
// PATH = "/account/api-key", body = "{}"
# Requires wallet signature auth โ€” use an SDK or raw script

GET /account/info

Get account details: credit balance, deposit address, usage stats. Works with any auth mode. The deposit_address and funding fields are the canonical source for where to send USDC โ€” bots should fetch this at runtime rather than relying on static documentation.

Response:

{
  "ok": true,
  "pubkey": "YourPublicKeyBase58...",
  "balance": 84521,
  "deposit_address": "CQKviPv5QupKhE6MuhSuqP4uh1JfrL93nxtcx23tLMRk",
  "funding": {
    "asset": "USDC",
    "network": "solana",
    "deposit_address": "CQKviPv5QupKhE6MuhSuqP4uh1JfrL93nxtcx23tLMRk",
    "credits_per_usdc": 10000,
    "credit_expiry_seconds": 864000,
    "credit_expiry_days": 10,
    "attribution": "send USDC from the same wallet pubkey used to activate the account"
  },
  "status": "active",
  "total_consumed_credits": 415479,
  "created_at": 1705312200,
  "last_deposit": {},
  "credit_batches": []
}

The deposit_address field is where you send USDC (SPL) on Solana mainnet. Minimum deposit: $0.01 USDC. Do not send SOL or other tokens.

const info = await rpc.getBillingAccount();
console.log("Balance:", info.balance);
console.log("Deposit address:", info.deposit_address);
console.log("Total consumed:", info.total_consumed_credits);
info = rpc.get_billing_account()
print("Balance:", info["balance"])
print("Deposit address:", info.get("deposit_address"))
print("Total consumed:", info["total_consumed_credits"])
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
req = urllib.request.Request(
    "https://rpc.nodius.xyz/account/info",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    info = json.loads(resp.read())
    print("Balance:", info["balance"])
const res = await fetch("https://rpc.nodius.xyz/account/info", {
  headers: { "X-Api-Key": "srpc_live_..." },
});
const info = await res.json();
console.log("Balance:", info.balance);
curl https://rpc.nodius.xyz/account/info \
  -H "X-Api-Key: srpc_live_..."

GET /account/ledger

Get credit transaction history (deposits, debits, adjustments). Query params: offset, limit.

Response:

{
  "entries": [
    {
      "type": "deposit",
      "amount": 10000,
      "balance": 10000,
      "description": "USDC deposit: 1.0 USDC",
      "timestamp": "2025-01-15T10:31:00Z"
    }
  ]
}
const ledger = await rpc.getLedger(50, 0);
for (const entry of ledger.entries) {
  console.log(entry.type, entry.amount, entry.description);
}
ledger = rpc.get_ledger(offset=0, limit=50)
for entry in ledger.get("entries", []):
    print(entry["type"], entry["amount"], entry["description"])
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
req = urllib.request.Request(
    "https://rpc.nodius.xyz/account/ledger?offset=0&limit=50",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    ledger = json.loads(resp.read())
const res = await fetch(
  "https://rpc.nodius.xyz/account/ledger?offset=0&limit=50",
  { headers: { "X-Api-Key": "srpc_live_..." } }
);
const ledger = await res.json();
curl "https://rpc.nodius.xyz/account/ledger?offset=0&limit=50" \
  -H "X-Api-Key: srpc_live_..."

GET /account/history

Get RPC usage history (method calls with credits consumed). Query params: offset, limit.

Response:

{
  "entries": [
    {
      "method": "getSlot",
      "credits": 1,
      "timestamp": "2025-01-15T10:32:00Z"
    }
  ]
}
const history = await rpc.getHistory(0, 50);
for (const entry of history.entries) {
  console.log(entry.method, entry.credits, entry.timestamp);
}
history = rpc.get_history(offset=0, limit=50)
for entry in history.get("entries", []):
    print(entry["method"], entry["credits"], entry["timestamp"])
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
req = urllib.request.Request(
    "https://rpc.nodius.xyz/account/history?offset=0&limit=50",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    history = json.loads(resp.read())
const res = await fetch(
  "https://rpc.nodius.xyz/account/history?offset=0&limit=50",
  { headers: { "X-Api-Key": "srpc_live_..." } }
);
const history = await res.json();
curl "https://rpc.nodius.xyz/account/history?offset=0&limit=50" \
  -H "X-Api-Key: srpc_live_..."

POST /account/confirm-deposit

Confirm a USDC deposit by providing the transaction signature. Speeds up credit assignment if automatic detection hasn't processed it yet.

Request body:

{
  "signature": "5xYz...base58..."
}

Response:

{
  "ok": true,
  "credits_added": 10000,
  "new_balance": 10000,
  "already_processed": false
}
const result = await rpc.confirmDeposit("5xYz...base58...");
console.log("Credits added:", result.credits_added);
result = rpc.confirm_deposit("5xYz...base58...")
print("Credits added:", result["credits_added"])
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
body = json.dumps({"signature": "5xYz...base58..."}).encode()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/account/confirm-deposit",
    data=body,
    headers={"Content-Type": "application/json", "X-Api-Key": API_KEY},
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read())
const res = await fetch("https://rpc.nodius.xyz/account/confirm-deposit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": "srpc_live_...",
  },
  body: JSON.stringify({ signature: "5xYz...base58..." }),
});
const result = await res.json();
curl -X POST https://rpc.nodius.xyz/account/confirm-deposit \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"signature":"5xYz...base58..."}'

Auth Endpoints

POST /auth/challenge

Request a challenge for session token auth. No authentication required.

Request body:

{
  "pubkey": "YourPublicKeyBase58..."
}

Response:

{
  "challenge": "a1b2c3d4e5f67890abcdef1234567890a1b2c3d4e5f67890abcdef1234567890",
  "expires_in": 300
}

The challenge is a 64-character hex string, valid for 300 seconds.

// The SDK handles the full challenge โ†’ sign โ†’ verify flow:
const token = await rpc.authenticate();
# The SDK handles the full challenge โ†’ sign โ†’ verify flow:
token = rpc.authenticate()
import json, urllib.request

body = json.dumps({"pubkey": "YourPublicKeyBase58..."}).encode()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/auth/challenge",
    data=body,
    headers={"Content-Type": "application/json"},
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read())
    print("Challenge:", result["challenge"])
const res = await fetch("https://rpc.nodius.xyz/auth/challenge", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ pubkey: "YourPublicKeyBase58..." }),
});
const { challenge } = await res.json();
curl -X POST https://rpc.nodius.xyz/auth/challenge \
  -H "Content-Type: application/json" \
  -d '{"pubkey":"YourPublicKeyBase58..."}'

POST /auth/verify

Submit a signed challenge to receive a session token. Sign the challenge hex string as UTF-8 bytes with your Ed25519 keypair (do not hex-decode it โ€” sign the 64-character hex text directly).

Request body:

{
  "pubkey": "YourPublicKeyBase58...",
  "challenge": "a1b2c3d4e5f67890...",
  "signature": "Base58EncodedSignature..."
}

Response:

{
  "token": "64-char-hex-session-token...",
  "expires_in": 3600
}

The token expires after 3600 seconds (1 hour) with a sliding TTL. See Authentication for the full flow with code examples.

curl -X POST https://rpc.nodius.xyz/auth/verify \
  -H "Content-Type: application/json" \
  -d '{"pubkey":"...","challenge":"...","signature":"..."}'

POST /auth/logout

Revoke the current session token. Requires Authorization: Bearer <token>.

Response:

{
  "status": "ok"
}
await rpc.authenticate(); // get a session token first
await rpc.logout();
rpc.authenticate()  # get a session token first
rpc.logout()
import json, urllib.request

TOKEN = "your_session_token_here"
req = urllib.request.Request(
    "https://rpc.nodius.xyz/auth/logout",
    data=b"{}",
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {TOKEN}",
    },
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
const res = await fetch("https://rpc.nodius.xyz/auth/logout", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${token}`,
  },
  body: "{}",
});
curl -X POST https://rpc.nodius.xyz/auth/logout \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token_here" \
  -d '{}'

Jito Endpoints

POST /jito/sendBundle

Submit a Jito bundle for MEV-protected transaction submission. Cost: 10 credits. Bundles must include a tip instruction to a Jito tip account.

const bundleId = await rpc.jitoSendBundle(["base58tx1...", "base58tx2..."]);
bundle_id = rpc.jito_send_bundle(["base58tx1...", "base58tx2..."])
import json, urllib.request

API_KEY = "srpc_live_your_api_key_here"
body = json.dumps({
    "jsonrpc": "2.0", "id": 1, "method": "sendBundle",
    "params": [["base58tx1...", "base58tx2..."]],
}).encode()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/jito/sendBundle",
    data=body,
    headers={"Content-Type": "application/json", "X-Api-Key": API_KEY},
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
const res = await fetch("https://rpc.nodius.xyz/jito/sendBundle", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "sendBundle",
    params: [["base58tx1...", "base58tx2..."]],
  }),
});
curl -X POST https://rpc.nodius.xyz/jito/sendBundle \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"jsonrpc":"2.0","id":1,"method":"sendBundle","params":[["base58tx1...","base58tx2..."]]}'

POST /jito/getBundleStatuses

Check status of previously submitted bundles. Cost: 2 credits.

const statuses = await rpc.jitoGetBundleStatuses(["bundleId1...", "bundleId2..."]);
statuses = rpc.jito_get_bundle_statuses(["bundleId1...", "bundleId2..."])
body = json.dumps({
    "jsonrpc": "2.0", "id": 1, "method": "getBundleStatuses",
    "params": [["bundleId1...", "bundleId2..."]],
}).encode()
# POST to https://rpc.nodius.xyz/jito/getBundleStatuses with X-Api-Key
const res = await fetch("https://rpc.nodius.xyz/jito/getBundleStatuses", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "getBundleStatuses",
    params: [["bundleId1...", "bundleId2..."]],
  }),
});
curl -X POST https://rpc.nodius.xyz/jito/getBundleStatuses \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"jsonrpc":"2.0","id":1,"method":"getBundleStatuses","params":[["bundleId1...","bundleId2..."]]}'

GET /jito/getTipAccounts

Get the list of valid Jito tip accounts. Cost: 1 credit.

const tipAccounts = await rpc.jitoGetTipAccounts();
tip_accounts = rpc.jito_get_tip_accounts()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/jito/getTipAccounts",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
const res = await fetch("https://rpc.nodius.xyz/jito/getTipAccounts", {
  headers: { "X-Api-Key": "srpc_live_..." },
});
curl https://rpc.nodius.xyz/jito/getTipAccounts \
  -H "X-Api-Key: srpc_live_..."

GET /jito/tipFloor

Get the current Jito tip floor (minimum tip amount). Cost: 1 credit.

const tipFloor = await rpc.jitoGetTipFloor();
tip_floor = rpc.jito_get_tip_floor()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/jito/tipFloor",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
const res = await fetch("https://rpc.nodius.xyz/jito/tipFloor", {
  headers: { "X-Api-Key": "srpc_live_..." },
});
curl https://rpc.nodius.xyz/jito/tipFloor \
  -H "X-Api-Key: srpc_live_..."

Bulk Endpoints

POST /bulk/getBalances

Batch balance lookups for multiple accounts.

Request body:

{
  "addresses": ["pubkey1...", "pubkey2...", "pubkey3..."]
}
const result = await rpc.bulkGetBalances(["pubkey1...", "pubkey2..."]);
result = rpc.bulk_get_balances(["pubkey1...", "pubkey2..."])
body = json.dumps({"addresses": ["pubkey1...", "pubkey2..."]}).encode()
# POST to https://rpc.nodius.xyz/bulk/getBalances with X-Api-Key
const res = await fetch("https://rpc.nodius.xyz/bulk/getBalances", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({ addresses: ["pubkey1...", "pubkey2..."] }),
});
curl -X POST https://rpc.nodius.xyz/bulk/getBalances \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"addresses":["pubkey1...","pubkey2..."]}'

POST /bulk/getTokenBalances

Batch token balance lookups for multiple owners.

Request body:

{
  "owners": ["pubkey1...", "pubkey2..."]
}
const result = await rpc.bulkGetTokenBalances(["pubkey1...", "pubkey2..."]);
result = rpc.bulk_get_token_balances(["pubkey1...", "pubkey2..."])
body = json.dumps({"owners": ["pubkey1...", "pubkey2..."]}).encode()
# POST to https://rpc.nodius.xyz/bulk/getTokenBalances with X-Api-Key
const res = await fetch("https://rpc.nodius.xyz/bulk/getTokenBalances", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({ owners: ["pubkey1...", "pubkey2..."] }),
});
curl -X POST https://rpc.nodius.xyz/bulk/getTokenBalances \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"owners":["pubkey1...","pubkey2..."]}'

POST /bulk/getTransactions

Batch transaction lookups.

Request body:

{
  "signatures": ["sig1...", "sig2...", "sig3..."]
}
const result = await rpc.bulkGetTransactions(["sig1...", "sig2..."]);
result = rpc.bulk_get_transactions(["sig1...", "sig2..."])
body = json.dumps({"signatures": ["sig1...", "sig2..."]}).encode()
# POST to https://rpc.nodius.xyz/bulk/getTransactions with X-Api-Key
const res = await fetch("https://rpc.nodius.xyz/bulk/getTransactions", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({ signatures: ["sig1...", "sig2..."] }),
});
curl -X POST https://rpc.nodius.xyz/bulk/getTransactions \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"signatures":["sig1...","sig2..."]}'

Webhooks

POST /webhooks

Create a webhook subscription for account/program monitoring.

Request body:

{
  "url": "https://your-server.com/webhook",
  "filter_type": "account",
  "filter_value": "pubkey1..."
}

Supported filter_type values: account, program, my_account (watches your own pubkey; filter_value optional), and lifecycle (fires on account-level events; requires a lifecycle_events array).

When filter_type is lifecycle, provide a lifecycle_events array selecting which events trigger webhooks:

{
  "url": "https://your-server.com/webhook",
  "filter_type": "lifecycle",
  "lifecycle_events": ["low_balance", "deposit.credited", "credits.expiring", "api_key.rotated"]
}

Available lifecycle events:

Event Fires When
low_balance Balance drops below 1,000 credits
deposit.credited A deposit is processed and credits granted
credits.expiring A credit batch is within 24h of expiry
api_key.rotated API key is rotated via POST /account/api-key

See Credits & Pricing for dedup behavior and payload details.

const webhook = await rpc.createWebhook(
  "https://your-server.com/webhook",
  "account",
  "pubkey1..."
);
webhook = rpc.create_webhook(
    url="https://your-server.com/webhook",
    filter_type="account",
    filter_value="pubkey1...",
)
body = json.dumps({
    "url": "https://your-server.com/webhook",
    "filter_type": "account",
    "filter_value": "pubkey1...",
}).encode()
# POST to https://rpc.nodius.xyz/webhooks with X-Api-Key
const res = await fetch("https://rpc.nodius.xyz/webhooks", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: JSON.stringify({
    url: "https://your-server.com/webhook",
    filter_type: "account",
    filter_value: "pubkey1...",
  }),
});
curl -X POST https://rpc.nodius.xyz/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{"url":"https://your-server.com/webhook","filter_type":"account","filter_value":"pubkey1..."}'

GET /webhooks

List all webhook subscriptions for your account.

const webhooks = await rpc.listWebhooks();
webhooks = rpc.list_webhooks()
req = urllib.request.Request(
    "https://rpc.nodius.xyz/webhooks",
    headers={"X-Api-Key": API_KEY},
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
const res = await fetch("https://rpc.nodius.xyz/webhooks", {
  headers: { "X-Api-Key": "srpc_live_..." },
});
const webhooks = await res.json();
curl https://rpc.nodius.xyz/webhooks \
  -H "X-Api-Key: srpc_live_..."

DELETE /webhooks/{id}

Delete a webhook subscription.

await rpc.deleteWebhook("abc123");
rpc.delete_webhook("abc123")
req = urllib.request.Request(
    "https://rpc.nodius.xyz/webhooks/abc123",
    headers={"X-Api-Key": API_KEY},
    method="DELETE",
)
with urllib.request.urlopen(req) as resp:
    print("Deleted:", resp.status)
const res = await fetch("https://rpc.nodius.xyz/webhooks/abc123", {
  method: "DELETE",
  headers: { "X-Api-Key": "srpc_live_..." },
});
console.log("Deleted:", res.ok);
curl -X DELETE https://rpc.nodius.xyz/webhooks/abc123 \
  -H "X-Api-Key: srpc_live_..."

POST /webhooks/{id}/rotate-secret

Rotate the signing secret for a webhook.

Response:

{
  "secret": "new_signing_secret..."
}
const result = await rpc.rotateWebhookSecret("abc123");
console.log("New secret:", result.secret);
result = rpc.rotate_webhook_secret("abc123")
print("New secret:", result["secret"])
req = urllib.request.Request(
    "https://rpc.nodius.xyz/webhooks/abc123/rotate-secret",
    data=b"{}",
    headers={"Content-Type": "application/json", "X-Api-Key": API_KEY},
    method="POST",
)
with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read())
const res = await fetch("https://rpc.nodius.xyz/webhooks/abc123/rotate-secret", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Api-Key": "srpc_live_..." },
  body: "{}",
});
const result = await res.json();
curl -X POST https://rpc.nodius.xyz/webhooks/abc123/rotate-secret \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: srpc_live_..." \
  -d '{}'

Public Endpoints

GET /health

Public health check. No authentication required.

Response (healthy):

{
  "status": "ok",
  "paid_traffic_ready": true,
  "agave_healthy": true,
  "service_profile": {
    "auth": {
      "wallet_signature": true,
      "api_key": true,
      "session_token": true
    },
    "services": {
      "hot_rpc": true,
      "send": true,
      "websocket": true,
      "yellowstone_lite": true,
      "heavy_indexed_rpc": true
    }
  },
  "timestamp": 1712000001,
  "solanaSlot": 285943216,
  "solanaBlockHeight": 265234567,
  "uptime": 864000,
  "nodeVersion": "4.1.0"
}

Response (degraded โ€” Agave behind or Redis circuit breaker open):

{
  "status": "degraded",
  "paid_traffic_ready": false,
  "agave_healthy": false,
  "fallback_active": true,
  "service_profile": { ... },
  "timestamp": 1712000001
}

Response (starting โ€” proxy not yet ready):

Returns HTTP 503:

{
  "status": "starting",
  "paid_traffic_ready": false,
  "agave_healthy": true,
  "service_profile": { ... },
  "timestamp": 1712000001
}
Status HTTP Code Description
ok 200 Healthy โ€” all systems operational
degraded 200 Operational but Agave is behind or a dependency is degraded; fallback RPC may be active
starting 503 Proxy not yet ready to serve paid traffic

Key fields: - paid_traffic_ready โ€” whether the proxy will accept and bill RPC requests - agave_healthy โ€” whether the local Agave validator is caught up - fallback_active โ€” present only when degraded; indicates requests are routing to the fallback RPC - service_profile โ€” shows which service gates are enabled (heavy indexed, archive, etc.)

const health = await rpc.health();
console.log("Status:", health.status);
health = rpc.health()
print("Status:", health["status"])
import json, urllib.request

with urllib.request.urlopen("https://rpc.nodius.xyz/health") as resp:
    health = json.loads(resp.read())
    print("Status:", health["status"])
const res = await fetch("https://rpc.nodius.xyz/health");
const health = await res.json();
console.log("Status:", health.status);
curl https://rpc.nodius.xyz/health

GET /pricing

Public endpoint returning the current credits-per-USDC rate. No authentication required.

Response:

{
  "ok": true,
  "credits_per_usdc": 10000
}
curl https://rpc.nodius.xyz/pricing
import json, urllib.request

with urllib.request.urlopen("https://rpc.nodius.xyz/pricing") as resp:
    pricing = json.loads(resp.read())
    print("Credits per USDC:", pricing["credits_per_usdc"])
const res = await fetch("https://rpc.nodius.xyz/pricing");
const pricing = await res.json();
console.log("Credits per USDC:", pricing.credits_per_usdc);
// No dedicated SDK method; fetch directly:
const res = await fetch("https://rpc.nodius.xyz/pricing");
const pricing = await res.json();
console.log("Credits per USDC:", pricing.credits_per_usdc);
# No dedicated SDK method; fetch directly:
import json, urllib.request
with urllib.request.urlopen("https://rpc.nodius.xyz/pricing") as resp:
    pricing = json.loads(resp.read())
    print("Credits per USDC:", pricing["credits_per_usdc"])

GET /capabilities

Public endpoint returning the endpoint capability contract. Useful for discovering whether archive or heavy indexed calls are enabled.

const caps = await rpc.capabilities();
console.log("Capabilities:", caps);
caps = rpc.capabilities()
print("Capabilities:", caps)
import json, urllib.request

with urllib.request.urlopen("https://rpc.nodius.xyz/capabilities") as resp:
    caps = json.loads(resp.read())
    print("Capabilities:", caps)
const res = await fetch("https://rpc.nodius.xyz/capabilities");
const caps = await res.json();
console.log("Capabilities:", caps);
curl https://rpc.nodius.xyz/capabilities

WebSocket

GET /ws

WebSocket endpoint for streaming subscriptions.

wss://rpc.nodius.xyz/ws

Authenticate via Authorization: Bearer <token> header or Sec-WebSocket-Protocol: auth.<token>, solana-rpc (both subprotocols must be sent together when using the Sec-WebSocket-Protocol option).

Supported subscriptions:

Method Unsubscribe Description
accountSubscribe accountUnsubscribe Account data changes
logsSubscribe logsUnsubscribe Transaction logs
programSubscribe programUnsubscribe Program account changes
signatureSubscribe signatureUnsubscribe Transaction signature status
slotSubscribe slotUnsubscribe Slot updates
rootSubscribe rootUnsubscribe Root slot updates
blockSubscribe blockUnsubscribe Block updates
slotsUpdatesSubscribe slotsUpdatesUnsubscribe Detailed slot status updates
voteSubscribe voteUnsubscribe Vote transaction notifications

See the Streaming Guide for detailed usage.


Yellowstone gRPC

High-performance streaming via Yellowstone geyser plugin.

grpc://rpc.nodius.xyz:10001

Authenticate via gRPC metadata: x-token: <session-token-or-api-key>. Cost: 60 credits per minute per active connection.

See the Yellowstone gRPC documentation for proto definitions and client libraries.


Response Headers

All authenticated endpoints include these headers on every response (including errors):

Header Description
X-Credits-Remaining Current credit balance (after this request's deduction)
X-Credits-Low true if balance < 1,000 credits
X-Request-Id Unique request identifier for debugging
X-Ratelimit-Limit Requests per second limit for this account
X-Ratelimit-Remaining Remaining requests in current window
X-Ratelimit-Reset Unix timestamp when the rate limit window resets

On 429 responses, Retry-After (seconds to wait before retrying) is also present.

Public endpoints (/health, /pricing, /capabilities) do not include credit or rate-limit headers.

Content Types