Data API
Wallet assets, transaction history, and NFTs in a single request — no RPC stitching. Archive queries are enabled per chain. Every response carries the block height and collection time, so results are easy to verify.
Endpoints and CU weights
All endpoints share one response envelope: success { "code": 1, "data": … }, Failed { "code": 400xxx, "message": "error message" }, and return X-Request-Id。
| Method | Path | Description | CU weight |
|---|---|---|---|
| GET | /api/v1/network/data/wallet/:address | Wallet assets (native coin + tokens, with decimals and raw integers) | 2 |
| GET | /api/v1/network/data/transactions/:address | Transaction history (paginated, 20 by default, up to 200) | 5 |
| GET | /api/v1/network/data/nft/:address | NFT holdings (with token_id and contract address) | 8 |
| GET | /api/v1/network/data/portfolio/:address | Portfolio data (per-chain value and allocation) | 10 |
| POST | /api/v1/network/archive/query | Archive queries (historical state and logs; must be enabled in the console) | 15–20 |
The Data API charges CU per request — default weights in the table above, current versions and unit prices in the public billing rules. It is metered and has its own SLA, separate from raw RPC. Failed requests and cache hits are never billed.
Look up wallet assets for an address
This page calls GET /api/v1/network/data/wallet/:address. Native balance is read in real time; EVM addresses query indexed tokens, Solana addresses query the two standard token programs. Retry later if a query fails.
Defaults to Ethereum mainnet or Solana mainnet; EVM addresses are normalized to lowercase and Solana addresses are kept as-is. Other configured networks can be selected with the chainId parameter.
The same call in three languages
All examples use cs_live_xxx as a placeholder. Never hardcode keys in clients: a key inside browser or mobile code is effectively public information.
curl -sS https://YOUR-GATEWAY-DOMAIN/api/v1/network/data/wallet/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045?chain_id=1 \
-H "Authorization: Bearer cs_live_xxx" \
-H "Accept: application/json"
# response (excerpt)
# {
# "code": 1,
# "data": {
# "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
# "chain_id": 1,
# "block_number": 21384022,
# "collected_at": "2026-10-26T15:04:05Z",
# "native": { "symbol": "ETH", "decimals": 18, "raw": "1234500000000000000" },
# "tokens": [
# { "contract": "0xa0b8…eb48", "symbol": "USDC", "decimals": 6, "raw": "2500000000" }
# ]
# }
# }
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 BASE = 'https://YOUR-GATEWAY-DOMAIN';
async function getWalletAssets(address, chainId = 1) {
const url = `${BASE}/api/v1/network/data/wallet/${address}?chain_id=${chainId}`;
const res = await fetch(url, {
headers: { 'Authorization': `Bearer ${CS_KEY}`, 'Accept': 'application/json' }
});
const payload = await res.json();
if (payload.code !== 1) {
// error code → user-facing message; 400016 means rate limited, so back off for the given seconds
const retryAfter = res.headers.get('Retry-After');
throw new Error(`request failed (${payload.code}): ${payload.message}`
+ (retryAfter ? `; retry in ${retryAfter} seconds` : ''));
}
// always use decimal strings for amounts to avoid floating-point rounding
return payload.data.tokens.map((t) => ({
symbol: t.symbol,
amount: t.raw, // raw integer; format with decimals only when displaying
decimals: t.decimals
}));
}
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
BASE = "https://YOUR-GATEWAY-DOMAIN"
def transactions(address, chain_id=1, page=1, limit=20):
resp = requests.get(
f"{BASE}/api/v1/network/data/transactions/{address}",
params={"chain_id": chain_id, "page": page, "limit": limit},
headers={"Authorization": f"Bearer {CS_KEY}"},
timeout=15,
)
payload = resp.json()
if payload.get("code") != 1:
# do not swallow errors: rethrow with the server message
raise RuntimeError(f"request failed ({payload.get('code')}): {payload.get('message')}")
data = payload["data"]
return data["items"], data["total"]
Replace YOUR-GATEWAY-DOMAIN with the gateway domain your deployer provides.
Example compute unit (CU) weights
The full weight table lives on the public billing rules page, versioned and dated; bills can be recalculated from the original rules.
| Method | Weight (CU) | Version |
|---|---|---|
| eth_chainId / net_version | 1 | v1 |
| eth_blockNumber | 1 | v1 |
| eth_getBalance | 2 | v1 |
| eth_call | 3 | v1 |
| eth_getLogs (≤ 1k blocks) | 10 | v1 |
| eth_getLogs (> 1k blocks) | +10 per 1k blocks | v1 |
| eth_sendRawTransaction | 5 | v1 |
| eth_getStorageAt (archive) | 20 | v1 |
| trace_* / debug_* | 50 | v1 |
Rate limits and error handling tips
| Error codes | Meaning | What to do |
|---|---|---|
| 400301 | Unsupported chain | See the Supported Networks page for the list |
| 400303 | Archive queries not enabled | Enable it per chain in the console, then retry |
| 400304 | Upstream node rate limit | The request was already retried or downgraded. Retry shortly |
| 400400 | Invalid address format | EVM and Solana follow different format rules; validate each separately |
| 400032 | Plan quota exceeded | Upgrade your plan or wait for the next billing period |
How the Data API differs from raw RPC
Because their cost structures differ. Raw RPC is a single forwarded request; the Data API needs aggregation, decoding, and indexing. Separate metering keeps unit prices and SLAs independent and stops volume on one side from hiding cost on the other.
No. All amounts are returned as raw integers (decimal strings), with decimals; formatting for display is done client-side to avoid precision loss from float accumulation.
Balance data updates with each new block and is usually visible seconds after it lands onchain; indexed data (transaction history, NFTs) has collection lag, and the block_number and collected_at in the response show how fresh the data is.
Yes, preferably keyed by chain_id + address + block_number as the cache key. The ChainBridge gateway also caches, and cache hits are free to customers.