JSON-RPC reference
ChainBridge Nodes is fully compatible with standard JSON-RPC 2.0. No client changes: point your client at a ChainBridge endpoint and add an API key. Routing, retries, circuit breaking, and rate limiting all happen in the gateway.
Endpoints and chain identifiers
| Protocol | Endpoint | Description |
|---|---|---|
| HTTP | POST /rpc/{chain} | Standard JSON-RPC 2.0 with batch array support |
| WebSocket | WS /ws/{chain} | Subscribe to new blocks, logs, and pending transactions. Opens chain by chain, following the enabled-capability list (see Supported Networks). Chains not yet enabled return 501 and are not billed. |
| Health check | GET /healthz | No auth. Liveness probe only — returns no customer data. |
- {chain}
- Short chain identifier, such as ethereum、bsc、 base、arbitrum、solana. See the Supported Networks page for the full list.
- Authentication
- Authorization: Bearer cs_live_xxx; keep the key on the server only.
- Request ID
- Every response includes X-Request-Id; include it when you contact support.
- Unsupported chain
- Returns 400301; unsupported methods return 400302。
Request examples
# read balance
curl -sS https://YOUR-GATEWAY-DOMAIN/rpc/ethereum \
-H "Authorization: Bearer cs_live_xxx" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","latest"]}'
# broadcast a transaction you signed yourself (ChainBridge never signs on your behalf)
curl -sS https://YOUR-GATEWAY-DOMAIN/rpc/ethereum \
-H "Authorization: Bearer cs_live_xxx" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"eth_sendRawTransaction","params":["0x02f8..."]}'
Replace YOUR-GATEWAY-DOMAIN with the gateway domain your deployer provides.
const CS_KEY = process.env.CS_API_KEY; // cs_live_xxx — server side only
const ENDPOINT = 'https://YOUR-GATEWAY-DOMAIN/rpc/ethereum';
async function rpc(method, params = [], attempt = 1) {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Authorization': `Bearer ${CS_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params })
});
const requestId = res.headers.get('X-Request-Id');
// rate limited: back off for the server-provided seconds, retry at most 3 times
if (res.status === 429 && attempt <= 3) {
const wait = Number(res.headers.get('Retry-After') || 5);
await new Promise((r) => setTimeout(r, wait * 1000));
return rpc(method, params, attempt + 1);
}
const payload = await res.json();
if (payload.error) {
// node error: keep requestId for troubleshooting; failed requests are not billed
throw new Error(`RPC failed (${payload.error.code}): ${payload.error.message}, request id ${requestId}`);
}
return payload.result;
}
const height = await rpc('eth_blockNumber');
console.log('Current block height (hex):', height);
Replace YOUR-GATEWAY-DOMAIN with the gateway domain your deployer provides.
import os
import requests
CS_KEY = os.environ["CS_API_KEY"] # cs_live_xxx — server side only
URL = "https://YOUR-GATEWAY-DOMAIN/rpc/ethereum"
# batch array: each element is billed its own CU (metered per item)
batch = [
{"jsonrpc": "2.0", "id": 1, "method": "eth_chainId", "params": []},
{"jsonrpc": "2.0", "id": 2, "method": "eth_blockNumber", "params": []},
{"jsonrpc": "2.0", "id": 3, "method": "eth_getBalance",
"params": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest"]},
]
resp = requests.post(
URL,
json=batch,
headers={"Authorization": f"Bearer {CS_KEY}"},
timeout=15,
)
for item in resp.json():
if "error" in item:
print(f"item {item['id']} failed: {item['error']['message']}")
else:
print(f"item {item['id']} result: {item['result']}")
Replace YOUR-GATEWAY-DOMAIN with the gateway domain your deployer provides.
Methods and compute unit weights
The weight table carries a version and effective date, so invoices can be recomputed under the original rules. Supported method sets differ by chain; below are the methods and weights currently in effect.
Batch requests and WebSocket
Batch requests
Send a JSON array as the request body; the response is an array in matching order.
- · Each element is metered separately, not once per batch
- · A failed element does not affect the others; errors are returned per item
- · Exceeding the payload size limit triggers 400018; split the batch into smaller requests
WebSocket subscriptions
Connect to /ws/{chain} then send a standard subscription request. WebSocket and archive are standard features, enabled chain by chain. Chains without them return 501 with an explanation and bill nothing.
- · Billed by connection time: 1 CU per connection per minute
- · Subscription messages are billed per message; reconnecting the same session within 5 minutes incurs no second connection fee
- · Connections idle for more than 10 minutes are closed by the server; clients must implement reconnection
Error codes
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
| 400300 | 503 | Node service temporarily unavailable | Failover is automatic; retry once |
| 400301 | 400 | Unsupported chain | See the Supported Networks page for the list |
| 400302 | 400 | Unsupported RPC method | Check whether the method is allowed for your plan and chain |
| 400303 | 400 | Archive queries not enabled | Enable archive per chain in the console |
| 400304 | 429 | Upstream node rate limit | Retried or degraded automatically; try again later |
| 400016 | 429 | Rate limit hit on your side | Back off using Retry-After and retry |
| 400018 | 413 | Payload too large | Shrink the request body or split the batch |
| 400032 | 403 | Plan quota exceeded | Upgrade your plan or wait for the next billing period |
| 500003 | 500 | Internal system error | Record X-Request-Id and contact support |
FAQ
No. ChainBridge Nodes only relays raw transactions you signed yourself (eth_sendRawTransaction). We never hold or custody assets, and never ask for private keys or seed phrases. Any page that asks for your private key is not us.
Node costs differ by chain: specs, archive requirements, response size. Weight tables are versioned per chain, and invoices are recomputed with the version in effect at call time.
No. Failed requests cost nothing, and cache hits bill zero CU.
The gateway probes block height and latency on every route. Past the lag threshold it fails over automatically, so your requests land on a healthier route with no code changes.