Batching and cost optimization
Per-call pricing rewards efficient request patterns. A few small habits cut spend dramatically, sometimes 5-20x for the same answer.
1. Use live-balance for batched ERC20 lookups
/api/chain/live-balance runs ETH balance + multiple ERC20 balances in a single call (server-side multicall under the hood). One $0.003 request beats N $0.003 requests:
# Cheap: one call for ETH + 3 tokens = $0.003
curl "https://api.onesource.io/api/chain/live-balance?address=0xd8da...&tokens=0xA0b8...,0xdAC1...,0x6B17..." \
-H "Authorization: Bearer $ONESOURCE_API_KEY"
# Expensive: four calls = $0.012, same data
curl "https://api.onesource.io/api/chain/erc20-balance?account=...&token=0xA0b8..." # $0.003
curl "https://api.onesource.io/api/chain/erc20-balance?account=...&token=0xdAC1..." # $0.003
curl "https://api.onesource.io/api/chain/erc20-balance?account=...&token=0x6B17..." # $0.003
# + a separate ETH balance call
The tokens parameter accepts a comma-separated list, typically up to 20 tokens per call before you should split.
Heads-up on response shape: the batched call returns each balance as a raw hex string and includes symbol and decimals but omits the name field, while erc20-balance returns a decimal string and includes name. If you're switching code from N single calls to one batched call, parse the hex (parseInt(balance, 16) or BigInt(balance)) and look up the human-readable token name elsewhere if you need it.
2. Use network-info instead of separate calls
/api/chain/network-info returns chain ID + latest block + current gas price in one $0.001 call. Don't call chain-id and block-number separately when you need the bundle, and don't reach for estimate-gas to get the chain-tip gas price (that endpoint estimates gas for a specific transaction body, not the tip).
| What you want | One call | Two calls |
|---|---|---|
| Chain ID + block + gas price | network-info = $0.001 | chain-id + block-number = $0.002 (and still no gas price) |
3. Pick the cheapest endpoint that answers your question
Endpoints are priced by complexity. Always check the API Reference for cost before wiring:
| Tier | Endpoints | Use when |
|---|---|---|
| $0.001 | block-number, chain-id, network-info | Tip / chain ID lookups |
| $0.003 | live-balance, block, erc20-balance, erc1155-balance, nft-owner, code, nonce, allowance, total-supply | Single-state reads |
| $0.004 | estimate-gas | Gas estimate for a specific transaction body |
| $0.005 | call, events, erc20-transfers, contract, receipt, storage, ens, proxy | Decoded reads, log queries |
| $0.008 | tx, nft-metadata, erc721-tokens | Heavy reads, metadata resolution |
| $0.010 | pending | Mempool reads |
A workload that calls chain/tx ($0.008) when chain/receipt ($0.005) would have answered the question is paying 60% more for the same outcome.
4. Cache deterministic results client-side
Some endpoint results are immutable. Cache them aggressively:
- Block headers (
/api/chain/block/{number}) for any block ≥ 64 blocks below tip: immutable after finality. - Transactions and receipts (
/api/chain/tx/{hash},/api/chain/receipt/{hash}): immutable after confirmation. - Contract bytecode (
/api/chain/code/{address}): immutable for non-upgradeable contracts; safe to cache for proxies between upgrade events. - NFT metadata (
/api/chain/nft-metadata): off-chain metadata via IPFS / Arweave is content-addressed; cache bytokenURIrather than re-fetching every call. - ENS resolution (
/api/chain/ens/{name}): cache for an hour or two; ENS records change rarely.
Even a 1-minute TTL on block lookups cuts cost dramatically for analytics workloads.
5. Don't poll the tip if you don't need to
If your app needs "is this transaction confirmed?", poll the receipt by hash, not the block tip. One $0.005 call replaces dozens of $0.001 tip checks plus per-block scans.
Watch the response shape, not the HTTP status. chain/receipt returns HTTP 200 whether the tx is confirmed or not; for an unknown hash you get {"data": {"result": null}, ...}. Polling on status 404 will loop forever. The correct check:
const { data } = await res.json();
if (data.result === null) return 'pending'; // not yet mined or unknown hash
return { status: 'confirmed', receipt: data.result };
(Same pattern with chain/tx, which returns {transaction, receipt} and uses data.transaction === null for the not-yet-mined case, but at $0.008 you're paying 60% more for the same answer if all you needed was "is it confirmed?".)
6. Batch event-log queries by topic + block range
/api/chain/events paginates eth_getLogs results. A single call with a wide to_block-from_block range and the right topic filters covers thousands of events for $0.005, much cheaper than N narrow queries. A 500-block window on a busy contract can easily return 10,000+ logs for a single $0.005 call.
The upstream node caps any single query at 20,000 log results. Overshoot and you get a 502 envelope error whose message includes the safe sub-range the node would have served: use it to drive adaptive pagination:
{
"data": null,
"error": {
"code": 502,
"message": "getLogs failed: RPC error -32602: query exceeds max results 20000, retry with the range 21675072-21675860"
},
"meta": { "endpoint": "/api/chain/events", "request_id": "..." }
}
Parse the suggested range out of the message and retry with that window, then continue from the next block. Add address or topic filters to keep result counts under the cap when you can.
7. For wallet-paid workloads, watch settlement fees too
x402 and MPP per-call USDC charges include on-chain settlement costs. At very low per-call prices ($0.001) the gas can be a non-trivial fraction of the spend. If you're running thousands of calls/minute, amortize the settlement so one deposit/envelope covers N calls:
- On Tempo (MPP): sessions: open a channel once, pay many calls under it.
- On Base (x402): batch settlement: the same model without leaving Base. Use the OneSource MCP Server's batch mode (no code) or build a batch-settlement client.
Quick checklist
- Replaced N ERC20 lookups with a single
live-balancecall. - Used
network-infofor chain-state checks instead of three separate calls. - Cached immutable results (finalized blocks, confirmed receipts, NFT metadata) client-side.
- Polling receipts by hash, not the tip.
- Picked the cheapest endpoint that answers each question (check
meta.cost_usdcto confirm). - For wallet-paid high-volume workloads: amortized settlement, MPP sessions on Tempo or x402 batch settlement on Base, instead of paying per call.