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 every admitted (rate-metered) response โ€” single calls and batches โ€” and on 429 responses, where Retry-After is also present. Requests rejected before admission (malformed JSON-RPC, unknown method) consume no rate units and carry no rate-window headers. Public endpoints (/health, /pricing, /capabilities) do not include credit or rate-limit headers.

Transaction v1 (SIMD-0385): Nodius fully supports Solana transaction v1. Pass maxSupportedTransactionVersion: 1 (integer) to getBlock, getTransaction, getBlocksWithLimit, and simulateTransaction to retrieve v1 transactions. Omitting it defaults to 0 and returns an error on blocks containing v1 transactions. V1 raises the transaction size limit from 1,232 to 4,096 bytes.

import { NodiusClient } from "@nodiusxyz/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 nodiusxyz 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 100 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, getEpochInfo, getEpochSchedule, getFeeForMessage, getFirstAvailableBlock, getGenesisHash, getHealth, getHighestSnapshotSlot, getIdentity, getInflationGovernor, getInflationRate, getLatestBlockhash, getLeaderSchedule, getMaxRetransmitSlot, getMaxShredInsertSlot, getMinimumBalanceForRentExemption, getMultipleAccounts, getRecentPerformanceSamples, getRecentPrioritizationFees, getSignatureStatuses, getSlot, getSlotLeader, getSlotLeaders, getStakeMinimumDelegation, getTokenAccountBalance, getTokenSupply, getTransactionCount, getVersion, getVoteAccounts, isBlockhashValid, minimumLedgerSlot, getPriorityFeeEstimate
READ_MEDIUM 5 simulateTransaction, getTransaction, getSignaturesForAddress, suggestPriorityFee, getClusterNodes
READ_HEAVY 25 simulateBundle, getBlock, getBlocks, getBlocksWithLimit, getTokenAccountsByOwner, getAssetsByOwner, getTransactionHistory โ€” getBlock methods served from hot-node retention (several hours of history; see the retention note below). Slots outside retention return Agave's native "cleaned up" error and are refunded.
SEND 2 sendTransaction, sendSmartTransaction
SEND_AND_CONFIRM outcome-priced sendAndConfirm โ€” multi-leg send (local RPC + Jito + TPU QUIC) with confirmation polling and auto-resend. Returns landing slot, time-to-confirm, and retry count. Full landing price charged only when the transaction confirms; a failed/timed-out attempt pays just the small base fee (equal to SEND). Live pricing: GET /pricing.
SEND_BUNDLE 10 sendBundle
ARCHIVE_HISTORY โ€” getEnrichedTransaction, explainTransaction, getConfirmedTransaction, getConfirmedSignaturesForAddress2 โ€” not enabled on this endpoint; calls return an error at no charge
DENIED โ€” requestAirdrop, getProgramAccounts, getSupply, getStakeActivation, getTokenLargestAccounts

getStakeActivation is not an Agave 4.3 RPC method (removed in Agave v2.0; see Solana migration guidance). This endpoint rejects it before billing; derive activation client-side from the stake account, StakeHistory sysvar, and current epoch. There is no single-method replacement, and a historical epoch requires archived state. getTokenLargestAccounts is a different incompatibility: Agave supports it, but this node lacks the SPL mint index; the unbounded scan can starve the RPC pool. It is disabled before billing, not offered as a heavy/slow read. Clients requiring mint-wide largest-account rankings need a separately indexed provider. Both return a local -32601 at no charge.

Hot-node retention: this endpoint serves at least the last several hours of history from a hot node (commonly 4โ€“8h; the window grows between ledger cleanup cycles). getSignaturesForAddress, getTransaction, and getTransactionHistory return empty/null results for older data. getBlock returns the node's native "cleaned up" error for slots outside retention โ€” those requests are refunded at no charge.

These are bounded findings, not a guarantee that every listed method or fixture is currently available. For example, an old slot can return Agave's cleaned-up error; an invalid token account or malformed transaction message can return -32602. getEnrichedTransaction and explainTransaction remain service-gated (archive_history: false) and return -32004 before billing on this endpoint.

โ€  getEnrichedTransaction and explainTransaction are not enabled on this endpoint (calls return an error at no charge). Other READ_MEDIUM methods are served by the hot node.

Method behaviors that trip up bots (verified against live prod):

Error Response

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

See Error Reference for all error codes.


Account Management

Note: /account endpoints are not per-account rate-metered (they are un-billed management reads/writes); IP-level limits apply to these routes.

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.

Optional ref_code field. If you were given a referral code, pass it here so the account is linked to it. The code is captured once, at account creation, and cannot be changed afterward โ€” pass it the first time you create the key.

{ "ref_code": "your-code" }

Unknown or expired codes are ignored silently (signup never fails on a bad code). Once linked, every USDC deposit from that wallet automatically earns its bonus credits โ€” no per-deposit action needed.

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..."}'

x402 Payment Endpoints

POST /x402/settle

Self-serve deposit settlement for x402 clients and agents. When a billable call returns 402 Payment Required, the body and the base64 payment-required header advertise USDC payment terms (minimum deposit $0.01). Build a partially-signed USDC transfer that pays those terms and submit it here โ€” Nodius verifies it and broadcasts it to the network. This endpoint is unauthenticated by design: it is the onboarding path for agents that have no account or credits yet.

Request body:

{
  "transaction": "<base64-encoded, partially-signed transaction (legacy or versioned)>"
}

The transaction must contain an SPL Token Transfer or TransferChecked that pays USDC to the Nodius deposit wallet's associated token account (ATA) โ€” NOT the wallet's default/standard token account (that account does not exist on-chain and settle rejects it). Derive the deposit ATA the same way the SDKs' deposit flows do: getAssociatedTokenAddress(depositWallet, USDC) using the payTo wallet pubkey from the 402 terms. A malformed settlement payload (not valid JSON) returns 400 Bad Request with a descriptive error field.

Verification (before broadcast):

Two settlement paths:

Path When Behavior
Self-paid (default) Fee payer is your wallet Transaction is verified and submitted as-is
Sponsored (opt-in) Fee payer is the Nodius signer wallet (advertised in extra.feePayer of the 402 terms) Strict whitelist: a single transferChecked instruction only, no ComputeBudget instructions. Nodius co-signs as fee payer and submits

Response:

{
  "success": true,
  "transaction": "5xKp...base58 signature...",
  "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
  "message": "Payment submitted. Credit your account via POST /account/confirm-deposit with this signature."
}

Credits are not assigned by this endpoint. After the transfer confirms, credit your account via POST /account/confirm-deposit with the returned signature โ€” crediting attributes by transfer authority (your wallet), preserving the same-wallet-pays-and-calls rule.

Errors:

Status Meaning
400 Verification failed: bad base64, invalid transaction, wrong destination/mint, amount below minimum, no payment instruction found, sponsored-whitelist violation, or malformed (unparseable) request body
502 Payment transaction rejected by the network
503 Settlement not configured
curl -X POST https://rpc.nodius.xyz/x402/settle \
  -H "Content-Type: application/json" \
  -d '{"transaction":"<base64 partially-signed transaction>"}'

With x402 auto-pay enabled in the SDK, the full handshake โ€” receive 402, settle, confirm deposit, retry โ€” runs automatically.


MCP

POST /mcp

Streamable-HTTP Model Context Protocol endpoint. Exposes Solana RPC as MCP tools so AI agents (Claude, Smithery, or any MCP client) can call JSON-RPC methods natively without a custom transport.

curl -X POST https://rpc.nodius.xyz/mcp \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: <your-key>" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"agent","version":"1.0"}}}'

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 '{}'

Premium RPC Methods

sendSmartTransaction

Send a fully-signed transaction through the multi-leg coordinator (local Agave RPC + Jito Block Engine + TPU QUIC). Unlike sendAndConfirm, it returns immediately after broadcast โ€” you track confirmation yourself (getSignatureStatuses).

Cost: per send, as published by GET /pricing.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendSmartTransaction",
  "params": [
    "<base64_or_base58_signed_transaction>",
    {
      "simulate": false,
      "priority_fee_urgency": "medium"
    }
  ]
}

Params:

Field Type Required Description
params[0] string yes Base64 or base58 encoded signed transaction
simulate boolean no Run a pre-send simulation first (default false). Failed simulations reject before the send and refund
priority_fee_urgency string no low | medium | high | critical โ€” used for the fee suggestion

Signatures are verified before billing; a corrupt or missing signature is rejected with -32602 at no charge.

Response: the transaction signature (base58 string), or an error object. The send is fire-and-forget โ€” poll getSignatureStatuses for landing.


sendAndConfirm

Send a transaction via the multi-leg coordinator (local Agave RPC + Jito Block Engine + TPU QUIC) and poll for confirmation. If the transaction hasn't landed after 8 slots (~3.2s), it's automatically re-sent. Up to 2 re-send attempts before timing out at 30 slots (~12s).

Cost: outcome-priced. The published landing and base prices are returned by GET /pricing. Successful confirmation is assessed the landing price; a definitive failed execution is assessed the base fee. A requested preflight rejection is fully refunded. An unknown outcome keeps its charge pending until recovery proves the result; a timeout is not proof of non-broadcast. The response's charged_credits reports the assessed or pending charge for that response. Check the original signature and settled account balance before retrying.

Preflight: the default is skipPreflight: true. Set skipPreflight: false to require signature-verifying simulation before any broadcast leg starts. A rejected or unavailable requested preflight stops submission; it does not authorize a fallback broadcast. Preflight success is a point-in-time check, not a guarantee against subsequent state changes or slippage.

Outcome: status distinguishes confirmed (successful execution), failed (a definitive on-chain execution error), and unknown (not yet resolved). A landed failure still incurs Solana network fees. Preserve the signature and reconcile unknown outcomes through signature status and getSendStatus; do not build or submit a replacement merely because the HTTP request failed.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendAndConfirm",
  "params": ["<base64_transaction>", { "encoding": "base64" }]
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "signature": "5xKp...",
    "confirmed": true,
    "slot": 285943216,
    "confirmations": 3,
    "landing_time_ms": 1240,
    "legs_attempted": ["local_rpc", "jito"],
    "retry_count": 0,
    "error": null
  }
}

Fields:

Field Type Description
signature string Transaction signature (base58)
confirmed bool Whether the transaction was confirmed on-chain
slot number|null Slot in which the transaction was confirmed
confirmations number|null Number of confirmations
landing_time_ms number|null Time from send to confirmation in milliseconds
legs_attempted string[] Which send legs were used (local_rpc, jito, tpu_quic)
retry_count number Number of re-send attempts (0 = landed first try)
charged_credits number Credits actually billed for this call (full landing price when confirmed is true; base fee only otherwise)
error string|null On-chain error message if the transaction landed but failed

getTransactionHistory

Fetch recent transactions for a Solana address, each enriched with decoded token transfers, SOL transfers, program interactions, and a natural language explanation. Batch-fetches and enriches concurrently for speed.

Cost: 25 credits

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTransactionHistory",
  "params": ["vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", { "limit": 10 }]
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "transactions": [
      {
        "signature": "5xKp...",
        "found": true,
        "slot": 285943200,
        "block_time": 1721342400,
        "fee": 5000,
        "error": null,
        "enriched": {
          "token_transfers": [...],
          "sol_transfers": [...],
          "program_interactions": [...],
          "summary": "Swapped 1.5 USDC for 0.0089 SOL via Jupiter v6"
        },
        "explanation": "Summary: Swapped 1.5 USDC for 0.0089 SOL via Jupiter v6\n..."
      }
    ],
    "count": 10
  }
}

Params:

Parameter Type Default Description
address string required Base58 Solana address
limit number 10 Number of transactions to return (max 100)

suggestPriorityFee

Suggest a priority fee (microlamports per compute unit) for a given urgency level. Cost: 5 credits.

Important: the global fee window is legitimately ~0 on mainnet when no fee market is active. For an actionable estimate on a congested account (Raydium/Jupiter AMM, pump.fun bonding curve), pass the writable accounts your transaction touches โ€” the estimate is then scoped to per-account contention. Estimates depend on the node's recent observations for those accounts. A valid window with no recorded fee demand can return zero; an upstream query failure returns an error rather than a zero estimate.

Request (urgency only):

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

Request (account-scoped โ€” recommended for bots):

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "suggestPriorityFee",
  "params": [{ "urgency": "high", "accounts": ["<writable-curve-or-market-account>"] }]
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "suggested_fee_microlamports": 27390,
    "urgency": "high",
    "all_levels": { "low": 10000, "medium": 18000, "high": 27390, "veryHigh": 35000, "sample_count": 150, "paid_slot_count": 9, "basis": "paid_slots" },
    "scoped_to_accounts": true
  }
}

Params:

Parameter Type Default Description
urgency string "medium" low, medium, high, or critical (maps to the 25th/50th/75th/90th percentile)
accounts string[] (global window) Writable accounts to scope the estimate to (max 128). Omit for the global 150-slot window

scoped_to_accounts tells you which window was used. Account-scoped levels use nonzero paid slots when at least three sampled slots paid a fee; all_levels.basis, sample_count, and paid_slot_count expose that choice. Otherwise the estimate includes all slots and may be zero. These are historical observations, not a landing guarantee; bound the total fee (price ร— compute-unit limit / 1,000,000) before sending.


Trade Execution

executeSnipe builds an unsigned pump.fun buy against the live bonding-curve state, fee-aware and slippage-guarded โ€” for both classic and v2 (buy_v2) bonding-curve launches. executeSell builds the matching unsigned sell for tokens you hold, including mayhem-mode launches and v2 curves (sell_v2); for WSOL-quoted v2 buys the builder funds and syncs a wrapped-SOL account for you. Non-SOL-quoted v2 curves settle in their quote asset and are rejected until supported. You sign both with your wallet and submit them. Nodius never takes custody of your keys.

Pricing: both methods charge a build fee plus a percentage of the trade's SOL notional, converted to credits at the live rate (GET /pricing gives current fees and conversion). The exact charge is returned in fee_credits. A large SOL budget therefore requires proportionally more credits; check your balance before building. If the build fails after billing, the charge is refunded automatically.

executeSnipe

Build an unsigned buy transaction for a pump.fun token, spending a fixed SOL budget. The quote is computed against the live curve state using the constant-product formula with protocol + creator fees netted out, so tokens_out matches what the on-chain buy_exact_sol_in will actually return.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "executeSnipe",
  "params": [{
    "mint": "TokenMintAddress...",
    "amount_sol": 0.1,
    "slippage_bps": 500,
    "priority_fee_micro_lamports": 100000
  }]
}

Params:

Field Type Required Description
mint string yes The token mint to buy
amount_sol number yes SOL budget to spend (e.g. 0.1)
slippage_bps number no Max slippage in basis points (default 500 = 5%)
priority_fee_micro_lamports number no Priority fee in micro-lamports (default 100000; see GET /pricing)

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "unsigned_tx_base64": "AQAAAA...",
    "tokens_out": 123456789,
    "max_sol_cost_lamports": 105000000,
    "fee_credits": 7750,
    "mint": "TokenMintAddress...",
    "bonding_curve": "CurvePDA..."
  }
}

Fields:

Field Type Description
unsigned_tx_base64 string Unsigned v0 transaction (create-ATA + buy). Sign with your wallet, then submit
tokens_out number Expected tokens received (base units), after fees
max_sol_cost_lamports number Slippage-guarded max SOL cost in lamports
fee_credits number Total credits charged for this call (build fee plus flow fee)
mint string The mint sniped
bonding_curve string The bonding-curve PDA targeted

executeSell

Build an unsigned sell transaction for pump.fun tokens you hold, quoting against the live curve state with the current FeeConfig fee tiers netted out. Works for standard pump.fun curves and mayhem-mode launches (Token-2022 mints). The sell fails closed before billing if your wallet does not hold the requested token amount.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "executeSell",
  "params": [{
    "mint": "TokenMintAddress...",
    "token_amount": 123456789,
    "slippage_bps": 500,
    "priority_fee_micro_lamports": 100000
  }]
}

Params:

Field Type Required Description
mint string yes The token mint to sell
token_amount integer or decimal string yes Positive amount in raw token base units (integer or decimal string for full u64 precision). Use the SDK sell_all/sellAll helper to read inventory; the RPC does not accept "all" or fractions.
slippage_bps number no Max slippage in basis points (default 500 = 5%)
priority_fee_micro_lamports number no Priority fee in micro-lamports (default 100000; see GET /pricing)

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "unsigned_tx_base64": "AQAAAA...",
    "sol_out_lamports": 99000000,
    "min_sol_output_lamports": 94050000,
    "fee_credits": 7425,
    "mint": "TokenMintAddress...",
    "bonding_curve": "CurvePDA..."
  }
}

Fields:

Field Type Description
unsigned_tx_base64 string Unsigned v0 transaction (sell). Sign with your wallet, then submit
sol_out_lamports number Estimated SOL output at the quoted state; not a guaranteed payout
min_sol_output_lamports number Slippage-guarded minimum SOL output in lamports
fee_credits number Total credits charged for this call (build fee plus flow fee)
mint string The mint sold
bonding_curve string The bonding-curve PDA targeted

Minimum sale size: sale fees are charged in basis points of the sale's gross SOL proceeds, so a sale fails when its gross SOL proceeds are so small that the bps fee rounds the net proceeds to zero. Such sales are rejected before billing with -32602, and the error names the exact lamports threshold (unbilled).

Bot flow: subscribeSignal identifies a mint โ†’ executeSnipe builds an unsigned buy โ†’ your wallet signs and submits it โ†’ verify on-chain landing and inventory โ†’ executeSell builds the unsigned exit for your position โ†’ your wallet signs and submits it. Builders return unsigned transactions; your wallet signs, broadcasts, and verifies landing. Check live send-path availability and pricing before depending on sendAndConfirm.


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. Bundle submission is best-effort: landing depends on Jito and network conditions, and the bundle ID does not guarantee inclusion.

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. Pricing: 1 credit per address (live rate: GET /pricing).

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. Pricing: 5 credits per owner (live rate: GET /pricing).

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. Pricing: 5 credits per signature (live rate: GET /pricing). The response has results (each entry contains its input signature and a transaction, which is null when absent) and errors (each entry contains a signature and an error). Results may arrive out of input order because lookups run concurrently; match by signature, not array position. An absent transaction is a completed lookup and is charged; backend lookup errors receive a refund for the failed items.

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.

Every delivery is signed: the X-Webhook-Signature header carries sha256=<hex> where <hex> is the HMAC-SHA256 of the bytes "{event_id}.{timestamp}.{body}" (note the two separating dots; body is the raw JSON payload) keyed with the webhook's secret. X-Webhook-Timestamp is the unix-seconds signing time, X-Webhook-Event-Id identifies the delivery event (evt_{webhook_id}_{job_id}), and X-Webhook-Id identifies the subscription. Verify the signature before trusting a payload; both SDKs ship a verify_webhook_signature helper. Account/program (tx-watch) webhooks deliver successful transactions only โ€” failed transactions never fire a delivery.

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

Only these four lifecycle events exist today. tx.confirmed / tx.failed / tx.received / token_transfer.received are not implemented โ€” subscribing with them returns unknown lifecycle event. To watch for an on-chain transaction to one of your own addresses, use filter_type: "my_account" (fires on signature activity on your pubkey) rather than a lifecycle event.

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_grpc": true,
      "heavy_indexed_rpc": true
    }
  },
  "timestamp": 1712000001,
  "solanaSlot": 285943216,
  "solanaBlockHeight": 265234567,
  "uptime": 864000,
  "nodeVersion": "4.1.0"
}

Response (degraded):

{
  "status": "degraded",
  "paid_traffic_ready": 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 a backend 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 - fallback_active โ€” present only when degraded; indicates requests are routing to the fallback RPC - service_profile โ€” shows which services are enabled

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 which services are enabled.

Response:

{
  "ok": true,
  "service_profile": {
    "auth": {
      "wallet_signature": true,
      "api_key": true,
      "session_token": true
    },
    "services": {
      "hot_rpc": true,
      "send": true,
      "websocket": true,
      "yellowstone_grpc": true,
      "heavy_indexed_rpc": true,
      "archive_history": false
    }
  }
}
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 *** header orSec-WebSocket-Protocol: auth., solana-rpc(both subprotocols must be sent together when using theSec-WebSocket-Protocol` option).

Connection lifecycle: the server sends a protocol-level Ping every 25 seconds, so an idle subscription is never reaped by the fronting proxy โ€” a healthy connection stays open indefinitely. Your client must answer Pongs (any standards-compliant WS library does this automatically). A 1006 abnormal close means the network path dropped, not an idle timeout โ€” just reconnect and re-subscribe. To never miss a signal across a reconnect, keep a small client-side buffer of recent signatures and de-dupe, or use the free GET /signals/recent snapshot as a gap-fill.

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; availability in /capabilities
slotsUpdatesSubscribe slotsUpdatesUnsubscribe Detailed slot status updates
voteSubscribe voteUnsubscribe Vote notifications; availability in /capabilities
subscribeSignal signalUnsubscribe nodius.xyz Signals firehose โ€” decoded on-chain signal events (see below)

See the Streaming Guide for detailed usage.


Signals Firehose

nodius.xyz Signals is a real-time firehose of decoded on-chain events, classified rule-based from the Geyser stream on a bare-metal Frankfurt node. A funded wallet gets instant access. Consume it two ways: the subscribeSignal WebSocket method, or signal webhooks (filter_type: "signal") for HTTP push delivery.

subscribeSignal (WebSocket)

Local (non-proxied) JSON-RPC method on wss://rpc.nodius.xyz/ws. Authenticate the connection the same way as any other WebSocket subscription (Authorization: Bearer *** header orauth., solana-rpc` subprotocols).

Cost: connect charge 5 credits per subscription, plus 1 credit per delivered event, billed from the same credit balance as RPC calls. signalUnsubscribe is free (0 credits). Live pricing: GET /pricing.

Event types (valid kinds values):

Kind Fires when
newPair A new pool/pair is created on a watched launch or DEX program (pump.fun create, Raydium AMM initialize, Orca Whirlpool). This is the event kind emitted today.

The event taxonomy (graduation, walletActivity, largeSwap) is defined and additional kinds are added as their instruction-level classifiers land โ€” the filter accepts them, and the stream emits each kind only once its classifier is live.

Filter params โ€” all optional, combined with AND semantics (an absent filter matches everything):

Param Type Description
kinds string[] Event kinds to receive (default: all)
mint string Only events for this token mint
programs string[] Only events from these program IDs

Subscribe example:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "subscribeSignal",
  "params": [
    {
      "kinds": ["newPair", "graduation"],
      "programs": ["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"]
    }
  ]
}

Response (result is the subscription id):

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

signalNotification example โ€” one per matched event:

{
  "jsonrpc": "2.0",
  "method": "signalNotification",
  "params": {
    "subscription": "sig_sub_1",
    "result": {
      "kind": "newPair",
      "program": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
      "signature": "5j7s...",
      "slot": 285943216,
      "mint": "TokenMintAddress...",
      "accounts": ["acct1...", "acct2..."],
      "quote_asset": "SOL",
      "curve_type": "classic",
      "complete": false
    }
  }
}

quote_asset identifies the denomination (SOL for classic Pump or a quote-mint address, including wrapped SOL So11111111111111111111111111111111111111112 for v2); curve_type is classic or v2 for recognized Pump launches. complete: false describes a newly created Pump curve at the event slot, not its current state. Fields are null when they cannot be established from the event; verify live curve state before building a trade. The free /signals/recent preview uses the same metadata fields.

Close the subscription with signalUnsubscribe:

{ "jsonrpc": "2.0", "id": 2, "method": "signalUnsubscribe", "params": ["sig_sub_1"] }
import { NodiusClient, NodiusWebSocket } from "@nodiusxyz/sdk";

// The SDK ships a WebSocket transport (NodiusWebSocket).
const rpc = new NodiusClient("https://rpc.nodius.xyz", { keypair, auth: "session" });
const ws = NodiusWebSocket.forClient(rpc);
await ws.connectUntilOpen();

// Returns a STRING subscription id (e.g. "sig_sub_1"), unlike the
// numeric ids of the Solana-native subscriptions.
const subId = await ws.subscribeSignal({
  kinds: ["newPair", "graduation"],
  programs: ["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"],
});
// Incoming messages with method "signalNotification" match SignalNotification.
// Later: await ws.unsubscribeSignal(subId);
from nodiusxyz import NodiusClient, NodiusWebSocket

# The SDK ships a WebSocket transport (NodiusWebSocket;
# requires the `websockets` extra: pip install nodiusxyz[ws]).
rpc = NodiusClient.from_base58("https://rpc.nodius.xyz", secret_key=SECRET)
with NodiusWebSocket.for_client(rpc) as ws:

    # Returns a STRING subscription id (e.g. "sig_sub_1"), unlike the
    # numeric ids of the Solana-native subscriptions.
    sub_id = ws.subscribe_signal(
        kinds=["newPair", "graduation"],
        programs=["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"],
    )
    # Later: ws.unsubscribe_signal(sub_id)
wscat -c "wss://rpc.nodius.xyz/ws" \
  -H "Authorization: Bearer $SESSION_TOKEN"
> {"jsonrpc":"2.0","id":1,"method":"subscribeSignal","params":[{"kinds":["newPair"]}]}

Signal webhooks

Prefer HTTP push over a persistent WebSocket? Register a webhook with filter_type: "signal" and a signal_events array of the event kinds you want. Deliveries reuse the standard webhook pipeline: HMAC X-Webhook-Signature header, exponential-backoff retry, and per-delivery billing (see GET /pricing for the live rate).

Request body:

{
  "url": "https://your-server.com/webhook",
  "filter_type": "signal",
  "signal_events": ["newPair", "graduation", "largeSwap"]
}

Each delivery POSTs the same SignalEvent shape shown in the signalNotification example above.

const webhook = await rpc.createSignalWebhook(
  "https://your-server.com/webhook",
  ["newPair", "graduation", "largeSwap"]
);
webhook = rpc.create_signal_webhook(
    url="https://your-server.com/webhook",
    events=["newPair", "graduation", "largeSwap"],
)
curl -X POST https://rpc.nodius.xyz/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: *** \
  -d '{"url":"https://your-server.com/webhook","filter_type":"signal","signal_events":["newPair","graduation"]}'

Yellowstone gRPC

High-performance streaming via Yellowstone geyser plugin.

grpc.nodius.xyz:443 (TLS)

Authenticate every request via gRPC metadata: x-token: <session-token-or-api-key>. Each established request or subscription, including unary calls, costs 1 credits, plus 60 credits per subsequent 60-second interval while active. GET /pricing publishes the live rates.

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