CRIUS Docs
API status Get API key
Use cases

Robinhood Chain API for wallets and portfolio apps

What a Robinhood Chain wallet or portfolio app needs from the Crius API: holdings, USD values, PnL, trade history and warnings about spam tokens.

Updated 3 min read

A wallet or portfolio app shows a user what they own, what it is worth and how they did: balances in ETH and tokens, USD values that don’t count spam as money, realized and unrealized PnL, and a trade history that names the token rather than the router. The Crius API returns the whole portfolio in one call, priced from on-chain pools, with a confidence level on each value. Apps that already read balances can use batch prices alone.

Endpoints a portfolio app uses

  • GET/wallet/{address} holdings with USD value and price_confidence, native ETH balance, realized and unrealized PnL over a window, win rate, gas spent and wallet tags. min_value_usd hides dust.
  • GET/wallet/{address}/trades every trade the wallet made across all tokens, newest first, filterable by token, side, min_usd and time.
  • GET/prices USD and ETH prices with 24h change for up to 100 tokens, when you have the balances already.
  • POST/tokens/batch names, logos and market data for the tokens in a portfolio, trimmed to the fields you show.
  • GET/token/{address}/candles price charts for a token detail screen.
  • GET/token/{address}/security a warning before the user sends, swaps or trusts a token.
  • The wallet:<address> stream channel: the wallet’s trades and transfers as they happen, so you refresh only when something changed.

Suggested architecture

  1. Balances you already have: price them in batches. POST /prices with up to 100 token addresses costs 1 credit and needs no key. A wallet with 300 tokens is three calls.

  2. Full portfolio: one profile call. /wallet/{address} (Starter, 5 credits) returns holdings, values and PnL together. Cache it per user and refresh when the wallet:<address> channel reports activity, not on every screen view.

  3. Value only what can be sold. Leave holdings with price_confidence: "low" out of the headline total, or show them under “unpriced”. Values are already capped at what each token’s price pool could pay out.

  4. Load history page by page. /wallet/{address}/trades with limit and cursor (5 credits per page). Each trade carries the USD value at trade time, so history never needs re-pricing.

As a budget: 10,000 users who each open the app three times a day, with one profile call per open, is 150,000 credits a day, about 4.5 million a month, which fits Starter’s 10,000,000. Refreshing on stream events instead of every open lowers that further.

A portfolio in one call

A smart account holding ETH and two fake USDG tokens

What does wallet 0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca hold, and what is it really worth?

GET/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca
curl "https://api.crius.sh/v1/robinhood/wallet/0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca" \
-H "x-api-key: $CRIUS_API_KEY"
Real response 200 Captured Oct 28, 2026 · 84 lines (arrays trimmed)
{
"data": {
"account_type": "smart_account",
"address": "0x3ac82d45ff9e4963cefbc3f74dbe293f34c927ca",
"delegate": "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b",
"holdings": [
{
"balance": 25,
"pnl": null,
"price_confidence": "low",
"token": {
"address": "0xfaaf8ffa8a29ceb6c85aff8d3961f65ae1bfc775",
"logo": null,
"symbol": "USDG"
},
"value_eth": 0.00000500064,
"value_usd": 0.0117789
},
{
"balance": 25,
"pnl": null,
"price_confidence": "low",
"token": {
"address": "0x06993ae1230d3755b54f25022028daa66772bc3d",
"logo": null,
"symbol": "USDG"
},
"value_eth": 0.00000500061,
"value_usd": 0.0117789
}
],
"holdings_truncated": false,
"holdings_value_usd": 0.0235578,
"is_contract": false,
"label": null,
"native_balance": 0.71473787532832551,
"native_value_usd": 1683.56,
"pnl": {
"avg_hold_time_s": null,
"best_trade": null,
"gas_scope": "all transactions sent by the wallet in the window",
"gas_spent_usd": 0.198772,
"realized_usd": 0,
"tokens_traded": 0,
"tokens_with_known_cost": 0,
"total_usd": 0,
"trades": 0,
"unrealized_usd": 0,
"volume_usd": 0,
"win_rate_pct": null,
"window": "30d",
"worst_trade": null
},
"profile": {
"first_seen_block": 91351250,
"first_seen_ts": 1792283372000,
"fresh": false,
"funded_by": {
"address": "0xd782268f600403843484b70710daee6846fc0db8",
"amount_eth": 0.008148386337868892,
"block": 87601233,
"label": "bridge:Relay solver",
"ts": 1791903516300
},
"history": "complete",
"tags": [
"dev",
"smart_account"
],
"tx_count": 14,
"wallet_age_days": 10.8994
}
},
"meta": {
"balances_block": 100406864,
"block": 100406864,
"block_age_ms": 1301,
"block_ts": 1793225084000,
"chain": "robinhood",
"finalized_block": 100397649,
"lag_ms": 59,
"safe_block": 100401329
}
}

What this tells you

  • account_type: "smart_account" with delegate: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b": an EIP-7702 wallet. profile.tags are dev and smart_account.
  • native_balance: 0.71473787532832551 ETH, worth native_value_usd: 1683.56. Almost all the value is ETH.
  • Both token holdings have symbol: "USDG" and balance: 25, but their addresses are 0xfaaf8ffa8a29ceb6c85aff8d3961f65ae1bfc775 and 0x06993ae1230d3755b54f25022028daa66772bc3d, not the real USDG 0x5fc5360d0400a0fd4f2af552add042d716f1d168. Each is valued at value_usd: 0.0117789 with price_confidence: "low".
  • An app that matched by symbol would show $50 of stablecoins here. The API values both at about a cent each: holdings_value_usd: 0.0235578.
  • profile.funded_by is a Relay solver (label: "bridge:Relay solver"): the wallet’s first ETH was bridged in.

Prices for balances you already know

Batch prices for a watchlist or a wallet's tokens

What are CHARGE (0x7b8892cea949bc60d494681d1bc3ea96071ab0b8) and WETH (0x0bd7d308f8e1639fab988df18a8011f41eacad73) worth right now?

GET/v1/robinhood/prices?tokens=0x7b8892cea949bc60d494681d1bc3ea96071ab0b8,0x0bd7d308f8e1639fab988df18a8011f41eacad73
curl "https://api.crius.sh/v1/robinhood/prices?tokens=0x7b8892cea949bc60d494681d1bc3ea96071ab0b8,0x0bd7d308f8e1639fab988df18a8011f41eacad73"
Real response 200 Captured Oct 28, 2026 · 34 lines (arrays trimmed)
{
"data": {
"0x0bd7d308f8e1639fab988df18a8011f41eacad73": {
"change_24h_pct": 0.286017,
"confidence": "high",
"eth": 1,
"liquidity_usd": 9953382,
"logo": null,
"source_pool": "0xbac3aa3b91584a53a579b3c999a56756e954e59247e497bad1d25a4334bde551",
"symbol": "WETH",
"usd": 2355.49
},
"0x7b8892cea949bc60d494681d1bc3ea96071ab0b8": {
"change_24h_pct": -0.171971,
"confidence": "medium",
"eth": 0.00000000165124,
"liquidity_usd": 5604.57,
"logo": "https://api.crius.sh/v1/robinhood/logo/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8.png?size=128",
"source_pool": "0x871b166182fd708d5d16a4daa9a42f05a653fa51ee2b195097f425e0d9ca4913",
"symbol": "CHARGE",
"usd": 0.00000388948
}
},
"meta": {
"block": 100406775,
"block_age_ms": 907,
"block_ts": 1793225075000,
"chain": "robinhood",
"finalized_block": 100397649,
"lag_ms": 35,
"missing": [],
"safe_block": 100401329
}
}

What this tells you

  • Results are keyed by address: WETH has usd: 2355.49 with confidence: "high" and liquidity_usd: 9953382; CHARGE has usd: 0.00000388948 with confidence: "medium".
  • change_24h_pct is ready for the green or red number next to each row (0.286017 for WETH, −0.171971 for CHARGE).
  • logo is a ready-to-use image URL for CHARGE and null for WETH: keep your own icons for the chain’s base assets.
  • meta.missing: []: an address without a price would be listed there rather than failing the call.
Batch prices reference Runs from your browser on the anonymous plan Data looks wrong? Report it
// Value a wallet from balances you already read over RPC (anonymous: no key needed).
async function valuePortfolio(balances /* { [tokenAddress]: amount } */) {
const res = await fetch("https://api.crius.sh/v1/robinhood/prices", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ tokens: Object.keys(balances).slice(0, 100) }),
});
const { data } = await res.json();
let total = 0;
const unpriced = [];
for (const [token, amount] of Object.entries(balances)) {
const p = data[token.toLowerCase()];
if (!p || p.confidence === "low") unpriced.push(token);
else total += amount * p.usd;
}
return { totalUsd: total, unpriced };
}

The balance × price above isn’t capped by pool depth, so a very large holding of a small token can still look worth more than its market could pay: use /wallet/{address} when that matters. How PnL is worked out is on the PnL page, and the guides cover holdings and PnL and trade history.

Frequently asked questions

How do I spot fake USDG and spam tokens in a wallet?

Match tokens by address, never by symbol: the real USDG is 0x5fc5360d0400a0fd4f2af552add042d716f1d168. Then use price_confidence on each holding (low means the token has no real market behind its price) and the /security check (risk, flags) before you count a token in the total or offer a swap.

Does the wallet API work with EIP-7702 smart accounts?

Yes. The wallet profile reports account_type (eoa, smart_account or contract) and, for smart accounts, the delegate contract. Trades made through the account's delegate are attributed to the account itself.

Which plan does a portfolio app need?

If your app already knows the balances (a wallet reads them from its own RPC), batch prices are enough: anonymous, 1 credit per 100 tokens. The wallet profile and wallet trades (holdings, PnL, history) need a Starter key and cost 5 credits per call.

Can I get a wallet's portfolio value over time?

There is no single portfolio-history call. Rebuild it from the wallet's trades (/wallet/{address}/trades) and each token's candles (/token/{address}/candles), or store the profile's holdings_value_usd yourself each day.

Get your free Crius API key

Every example on this page works with a free key: higher rate limits than the anonymous plan, holders, wallets and the WebSocket stream. No card needed.

  • Robinhood Chain (4663) only, from our own archive nodes
  • REST + WebSocket
  • Pay on-chain or by card