CRIUS
ComparisonRPCExplainerHistorical dataHoldersRobinhood Chain

Robinhood Chain REST API vs RPC vs Indexed Data API

One Robinhood Chain question answered three ways: raw JSON-RPC, a REST wrapper and an indexed data API, with tested code, timings and when to use each.

Crius 10 min read

Use JSON-RPC (a node or RPC provider) to send transactions and read current contract state; use an indexed data API to read history, prices, trades and holders. “REST” only describes the transport: a REST API can be a thin wrapper around RPC or a front end to a full index, and that difference, not the URL style, decides how much work is left for you.

This post answers one concrete question three ways on Robinhood Chain: what is a token trading at, and what are its latest trades? It uses raw RPC, then a REST wrapper, then an indexed API. All code was run in October 2026 against Robinhood’s public RPC and the Crius API, and the timings are real. For a shorter side-by-side summary, see the RPC vs indexer comparison; provider-by-provider pages are in the comparison hub.

Three ways to read the chain

JSON-RPC REST wrapper Indexed data API
What it is The node’s own interface: eth_getLogs, eth_call, eth_getBlockByNumber HTTP endpoints over node data: balances, transfers, token metadata A database built by replaying the chain, served as decoded answers
Data shape Raw blocks, receipts, logs, hex Normalized, but mostly what the node already knows Decoded trades, prices, holders, candles, launches
History Whatever the node keeps; archive nodes keep everything Varies Precomputed, usually from genesis
Writes Yes (eth_sendRawTransaction) Sometimes No
Your work Decode, price, attribute, aggregate, handle reorgs Some aggregation Mostly none
Robinhood Chain examples Public RPC, Alchemy, QuickNode, Goldsky Edge Alchemy’s Token, Transfers and Prices APIs Crius, Bitquery, GoldRush

The provider landscape is covered in the Robinhood Chain data API comparison. Here we focus on what each layer actually takes to build on.

Approach 1: raw JSON-RPC

Robinhood Chain is an Arbitrum Orbit chain (now called Arbitrum Dedicated Blockchains) and speaks standard Ethereum JSON-RPC. Robinhood runs a free public endpoint at https://rpc.mainnet.chain.robinhood.com, shared and rate-limited, which is enough for this experiment.

Take Charge AI (CHARGE), a token that graduated from the Pons launchpad into a Uniswap v4 pool. Uniswap v4 keeps every pool inside one singleton contract, the PoolManager, and emits a Swap event whose first indexed topic is the 32-byte pool id. So to read the pool’s swaps you filter the PoolManager’s logs by event signature and pool id, then decode the price from sqrtPriceX96:

// The RPC way: read Uniswap v4 Swap logs for one pool straight from a node and derive the price yourself.
const RPC = "https://rpc.mainnet.chain.robinhood.com"; // public, rate-limited
const POOL_MANAGER = "0x8366a39cc670b4001a1121b8f6a443a643e40951"; // Uniswap v4 singleton
// keccak256("Swap(bytes32,address,int128,int128,uint160,uint128,int24,uint24)")
const SWAP = "0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f";
const POOL_ID = "0x871b166182fd708d5d16a4daa9a42f05a653fa51ee2b195097f425e0d9ca4913"; // ETH / CHARGE
async function rpc(method, params) {
const res = await fetch(RPC, { method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }) });
const body = await res.json();
if (body.error) throw new Error(`${method}: ${body.error.message}`);
return body.result;
}
const head = parseInt(await rpc("eth_blockNumber", []), 16);
const span = Number(process.env.SPAN ?? 50_000); // ~1.4 hours of blocks at ~100 ms
const t0 = Date.now();
const logs = await rpc("eth_getLogs", [{ address: POOL_MANAGER, topics: [SWAP, POOL_ID],
fromBlock: "0x" + (head - span).toString(16), toBlock: "0x" + head.toString(16) }]);
console.log(`${logs.length} swaps in the last ${span} blocks, fetched in ${Date.now() - t0} ms`);
if (logs.length) {
// data = amount0, amount1, sqrtPriceX96, liquidity, tick, fee (32 bytes each)
const words = logs.at(-1).data.slice(2).match(/.{64}/g);
const sqrtPriceX96 = BigInt("0x" + words[2]);
// currency0 = native ETH, currency1 = CHARGE, both 18 decimals: ETH per CHARGE = 2^192 / sqrtP^2
const ethPerToken = Number((2n ** 192n * 10n ** 18n) / (sqrtPriceX96 * sqrtPriceX96)) / 1e18;
console.log(`last price: ${ethPerToken.toExponential(5)} ETH per CHARGE (still needs an ETH/USD price from another pool)`);
}
$ SPAN=50000 node rpc_swaps.mjs
0 swaps in the last 50000 blocks, fetched in 270 ms
$ SPAN=500000 node rpc_swaps.mjs
2 swaps in the last 500000 blocks, fetched in 242 ms
last price: 2.32493e-9 ETH per CHARGE (still needs an ETH/USD price from another pool)

It works, and it’s fast for one pool. But look at what this script quietly assumed:

  1. That you know the pool. CHARGE also had a Pons bonding curve before graduation; other tokens have v2, v3 and v4 pools at once. Choosing the pool that should define “the price” means comparing liquidity across every market, in-range liquidity only for v3 and v4.
  2. That the quote is ETH. Here it is, so you still need ETH/USD from yet another pool. Tokenized stocks are often quoted in USDG or in other tokens, which takes a second hop.
  3. That you know the decimals and the token order. currency0/currency1 ordering decides whether the price is P or 1/P.
  4. That “latest trades” means swaps in this pool. The sender in a v4 Swap is the router or the universal router, not the person trading. Finding the real trader means following token transfers inside the transaction.
  5. That a small range is enough. The public endpoint accepts at most 10 million blocks per eth_getLogs (about 12 days). In our test, 5 million blocks returned 326 swaps in about a second, and 20 million blocks was rejected: “query spans 19999985 blocks, but only 10000000 are allowed”. Full history means paging from block 0.
  6. That volume is small. Across all v4 pools, the last 2,000 blocks (about 3.5 minutes) held 1,970 swaps. Chain-wide, that’s well over a million Swap logs a day to fetch, decode and price.

Each of these is solvable. Solved together, with Uniswap v2 and v3, launchpad curves, holder balances and reorg handling, they add up to an indexer.

Approach 2: a REST wrapper over node data

Node providers add HTTP APIs on top of their nodes, such as token balances for a wallet, transfer history or a token price. On Robinhood Chain, Alchemy lists its Token, Transfers, Prices and Webhooks APIs as supported, and its Debug and Trace APIs as not currently supported (Alchemy network page).

These APIs are a real step up from raw RPC for wallet-centric questions: “what does this address hold?” or “what transfers did it make?”. They don’t cover market questions: which pool a price comes from, who traded through a router, holder distribution, bonding-curve progress or candles. They are REST, but they aren’t an index of DEX activity. If your app is a wallet or a payments flow, this layer may be all you need.

Approach 3: an indexed data API

An indexer replays every block once, decodes every event it understands, keeps derived state (balances, pool reserves, prices, holder lists), and serves answers to questions. The same question as approach 1:

Terminal window
curl "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/price"
{
"data": {
"symbol": "CHARGE",
"usd": 5.50044e-06,
"eth": 2.32493e-09,
"dex": "uniswap_v4",
"source_pool": "0x871b166182fd708d5d16a4daa9a42f05a653fa51ee2b195097f425e0d9ca4913",
"liquidity_usd": 6679.58,
"confidence": "medium"
},
"meta": { "block": 96713051, "lag_ms": 2, "safe_block": 96707359, "finalized_block": 96703786 }
}

The ETH price matches the one we derived by hand (2.32493e-9), and the response already includes the USD price, the pool it came from, that pool’s liquidity and how much to trust it. For the latest trades:

Terminal window
curl "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/trades?limit=1"
{
"data": [{
"type": "sell",
"trader": "0x6034c50c16c5d73589de7488e236df403a5c5591",
"signer": "0xabb2acd3be814a80e502575d6c1dc5f789e9cd10",
"amount_base": 133448.2087111015,
"amount_quote": 0.000310325791039145,
"price_usd": 5.53267e-06,
"value_usd": 0.738324,
"dex": "uniswap_v4",
"fees": { "gas": { "l2_eth": 9.6821e-06, "l1_eth": 0, "usd": 0.0229124 }, "token_tax": { "pct": 0 } },
"finality": "finalized",
"block": 96404580
}],
"meta": { "next_cursor": "96303852-10" }
}

Note trader and signer differ: the transaction was signed by an executor contract on the user’s behalf, and the index attributed the trade to the wallet that actually sold. That’s step 4 of the RPC list, already done (how attribution works). Full history is a cursor away: walking CHARGE’s 5,104 trades since launch took about 21 seconds without an API key (full export tutorial).

What you would build to replace the index

If you choose RPC for reads, these are the parts you sign up for. The figures are from building Crius’s own Robinhood Chain index:

Component What it involves Scale on Robinhood Chain
Archive node A Nitro node with full state history ~2.8 TB at block ~96.7M
Backfill Every block’s receipts ~10 h at 2,200–2,900 blocks/s with 32 workers on a local node
Traces Internal ETH transfers, call trees ~88 h at 227–388 blocks/s
Decoders Uniswap v2, v3, v4 (with hooks), each launchpad’s curve 5 DEX and curve venues with volume in the last 24 h
Pricing graph Pick each token’s pool, hop through ETH/USDG/other quotes ~2.4M tokens, ~3.1M pools
Attribution Real trader behind routers, solvers, bundlers Every swap
Holder state Balances from every transfer ~1.8 billion transfers
Reorg handling Roll back soft-confirmed blocks, track safe and finalized Continuous
Storage and serving Columnar store, caches, rate limits ~190 GB compressed, ~12 billion rows

None of this is exotic, and some teams should build it, especially if they need a custom schema or want no dependency on a third party. It is, however, weeks of work before the first useful query, plus ongoing operations.

The pattern most apps use: index for reads, RPC for writes

You rarely have to choose only one. A common production setup:

  • Reads (prices, charts, trades, holders, history) from an indexed API;
  • Writes (swaps, transfers, contract calls) through an RPC provider;
  • Spot checks of critical numbers against the node before acting on them.

The spot check is cheap. This script reads a token’s top holders from the indexed API, then verifies each balance on-chain with eth_call at the same block the API answered for:

// Hybrid: read holders from the indexed API, spot-check their balances against a node with eth_call.
const API = "https://api.crius.sh/v1/robinhood";
const RPC = "https://rpc.mainnet.chain.robinhood.com";
const TOKEN = "0x7b8892cea949bc60d494681d1bc3ea96071ab0b8"; // CHARGE, 18 decimals
const res = await fetch(`${API}/token/${TOKEN}/holders?limit=5`, { headers: { "x-api-key": process.env.CRIUS_API_KEY } });
const { data, meta } = await res.json();
for (const h of data.holders) {
// balanceOf(address) = 0x70a08231 + 32-byte padded address, read at the block the API answered for
const call = { to: TOKEN, data: "0x70a08231" + h.address.slice(2).padStart(64, "0") };
const body = await (await fetch(RPC, {
method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_call", params: [call, "0x" + meta.block.toString(16)] }),
})).json();
const onchain = Number(BigInt(body.result)) / 1e18;
const diff = Math.abs(onchain - h.balance) / Math.max(h.balance, 1e-18);
console.log(`${h.address} ${(h.label ?? "").padEnd(24)} api ${h.balance.toFixed(2)} node ${onchain.toFixed(2)} ${diff < 1e-9 ? "match" : "MISMATCH"}`);
}
0x8366a39cc670b4001a1121b8f6a443a643e40951 uniswap_v4_pool_manager api 606060998.96 node 606060998.96 match
0x267444d099b10fb5ed7c3cc7b7c767adca574952 pons_v2_launch_locker api 81632653.06 node 81632653.06 match
0xcae68632857541d7b625f5af57149a77c80b15f7 api 35961843.24 node 35961843.24 match
0x346f5777f5652bd561dcd830e638bc40d3e1cf3d api 34089272.30 node 34089272.30 match
0x000000000000000000000000000000000000dead api 29840058.77 node 29840058.77 match

Pinning the RPC read to meta.block matters: on a chain with a block every ~105 ms, comparing “latest” on two systems compares two different moments. The labels in the output also show what the index adds: the largest “holder” is the Uniswap v4 PoolManager (the pool’s liquidity), the second is the Pons launch locker, and the fifth is the burn address. A naive top-10 share would count all three as investors. More on that in the token holders API.

When to use what

Situation Use
Sending transactions, reading contract state right now RPC
A wallet: balances, transfers, approvals RPC + REST wrapper
Prices, charts, trade tapes, screeners Indexed API
Holder analysis, snapshots, distributions Indexed API
Launchpad tracking: curves, graduations Indexed API
Custom research on unusual contracts RPC, or your own indexer
Verifying a number before you move money Indexed API, then RPC at the same block

FAQ

Is a REST API faster than RPC? Not inherently; both are HTTP. The speed difference comes from precomputation. One indexed call returns what would take many RPC calls plus decoding, so it’s faster end to end even when a single RPC call is quick.

Can I get historical token prices from RPC? Only by replaying swap events and reconstructing pool state at each block, then converting through quote prices. An indexed API serves this directly as OHLCV candles from 1 second to 1 day.

Does Robinhood Chain have a free RPC? Yes. Robinhood runs a public endpoint at rpc.mainnet.chain.robinhood.com, shared and rate-limited, intended for testing and light use. Production apps usually use a dedicated RPC provider.

What’s the biggest hidden cost of building on RPC? Correctness, not compute: choosing the right price source, attributing trades routed through contracts, and handling reorgs. Each is easy to get almost right and hard to get exactly right.

Do I need an archive node to read historical state? For eth_call at old blocks, yes (or an archive-capable provider). For history that’s already in an index (trades, transfers, holder snapshots), no.

Build with Crius

Robinhood Chain data in one API

Free plan with 1M credits a month. Your key in under a minute.

Related APIs

More from the blog