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
- All request bodies must be
application/json - All responses are
application/json - The
/wsendpoint uses the WebSocket protocol - The gRPC endpoint uses Protocol Buffers