vike
API EndpointsOn-chain

/wallets/{address}/balances

Current token balances for a wallet on Ethereum and BSC, read from the chain at request time, with a USD value where the token is priced.

GET https://data.vike.io/v1/onchain/wallets/{address}/balances

Current token balances for one wallet, read from the chain when you ask. The candidate token set comes from our own transfer index; the amounts come from balanceOf on the node, so a balance is what the contract says it is at that block rather than a figure derived from history.

Two chains, and the list is the answer — not a default

ethereum and bsc are the chains this route can answer for, because they are the two the transfer index covers and the index is what supplies the candidate tokens. chains_scanned in the response names what was actually read.

Ask for a chain outside that set and it comes back named in chain_errors — not silently scanned and empty. A wallet reported as holding nothing because nobody looked is the one answer this endpoint must never give.

cURL
# what a wallet holds right now, keeping only holdings worth at least 3,000 USD
curl -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=3000"

# the simplest call: both chains, and an omitted floor resolves to 1 USD rather than 0
curl -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances"

# the complete list, unpriced airdrops included: an explicit 0 is honoured, and a long-lived wallet can return thousands of rows
curl -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=0"

# one chain only
curl -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=bsc&min_usd=100"

# an alias works for a chain name: eth, mainnet, bnb and binance are accepted
curl -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=eth,bnb&min_usd=100"

# a chain this route does not scan is named in chain_errors rather than reported as an empty wallet
curl -s -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=base,ethereum&min_usd=3000" \
  | jq '{chains_scanned, chain_errors}'

# the ten most valuable priced holdings: the route lists largest USD value first with unpriced ones last, and native ETH and BNB carry no price here, so they sort after every priced holding and fall outside the first ten when there are more; total_usd sums priced holdings only, so read it as a floor
curl -s -H "X-API-Key: $VIKE_API_KEY" \
  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=1" \
  | jq '.holdings[:10]'
addresspathrequired
Wallet address, 0x-prefixed, exactly 42 characters
chainsqueryoptional
Comma-separated. ethereum and bsc; the aliases eth, mainnet, bnb and binance are accepted. Omit for both. Any other chain is reported in chain_errors.
min_usdqueryoptional
Hide holdings worth less than this. Omitted resolves to 1 (USD); an explicit 0 filters nothing and lists every holding. ⚠ Any floor above 0 also excludes holdings we cannot price, since an unpriced holding cannot be shown to clear it. The chain's native coin is the exception: it is always listed, unpriced, whatever the floor.

A live response, captured 2026-09-04 for vitalik.eth at min_usd=3000.

Example response
{
  "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  "total_usd": 10532.82,
  "chains_scanned": ["ethereum"],
  "holdings": [
    {
      "chain": "ethereum",
      "symbol": "ENS",
      "name": "Ethereum Name Service",
      "address": "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72",
      "balance": 1144.0360760636715,
      "balance_raw": "1144036076063671431479",
      "usd_value": 6843.62,
      "usd_price": 5.982
    },
    {
      "chain": "ethereum",
      "symbol": "WETH",
      "name": "Wrapped Ether",
      "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "balance": 1.461898164019089,
      "balance_raw": "1461898164019088870",
      "usd_value": 3689.2,
      "usd_price": 2523.57
    },
    {
      "chain": "ethereum",
      "symbol": "WEI",
      "name": "Wei Name Service",
      "address": "0x0000000000696760e15f265e828db644a0c242eb",
      "balance": 1.0,
      "balance_raw": "1",
      "usd_value": null,
      "usd_price": null
    }
  ]
}

usd_value: null is a holding, not an error

A balance is measured; a price is looked up, and most tokens do not have one — 434 of the tokens traded on Ethereum carry a price feed, out of the hundreds of thousands that exist. A holding we cannot price is still listed, with usd_value and usd_price both null, because dropping it would under-report the wallet.

total_usd therefore sums only the priced holdings. Read it as a floor, never as net worth, and never infer "worthless" from a null — it means unpriced here.

This is why min_usd above 0 drops unpriced holdings, and why it defaults to 1

A floor says "worth at least this much", and an unpriced holding cannot show that. Most tokens in a long-lived wallet are unpriced airdrops — vitalik.eth carries over seven thousand of them. A value floor that kept everything it could not price would return all of them and filter almost nothing.

min_usd=0 is the way to ask for the complete list, and it is honoured: an omitted floor and an explicit zero are different requests.

balance_raw is the integer the contract returned, as a string. Use it when you need exactness: balance is that integer divided by the token's decimals and is a float, so it loses precision on large balances.

The native coin

ETH on ethereum and BNB on bsc are included when non-zero, with address: null — a native coin has no contract. Neither is priced by this route today, so both carry usd_value: null.

What this route cannot see

A token the wallet holds but which never appeared in our transfer index is invisible here: the index supplies the candidate list, and balanceOf is only called for tokens on it. That fails toward omission — a missing row, never a wrong number.

It is the narrower of the two available gaps, and deliberately chosen. Until 2026-09-04 this route was served by a third-party API; a period when that vendor was unavailable returned 200 with an empty holdings array and total_usd: 0.0, which reads exactly like an empty wallet. Both shapes are on the changelog.

cURL
# what a wallet holds right now, keeping only holdings worth at least 3,000 USDcurl -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=3000"# the simplest call: both chains, and an omitted floor resolves to 1 USD rather than 0curl -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances"# the complete list, unpriced airdrops included: an explicit 0 is honoured, and a long-lived wallet can return thousands of rowscurl -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=0"# one chain onlycurl -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=bsc&min_usd=100"# an alias works for a chain name: eth, mainnet, bnb and binance are acceptedcurl -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=eth,bnb&min_usd=100"# a chain this route does not scan is named in chain_errors rather than reported as an empty walletcurl -s -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?chains=base,ethereum&min_usd=3000" \  | jq '{chains_scanned, chain_errors}'# the ten most valuable priced holdings: the route lists largest USD value first with unpriced ones last, and native ETH and BNB carry no price here, so they sort after every priced holding and fall outside the first ten when there are more; total_usd sums priced holdings only, so read it as a floorcurl -s -H "X-API-Key: $VIKE_API_KEY" \  "https://data.vike.io/v1/onchain/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balances?min_usd=1" \  | jq '.holdings[:10]'
Example response
{  "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",  "total_usd": 10532.82,  "chains_scanned": ["ethereum"],  "holdings": [    {      "chain": "ethereum",      "symbol": "ENS",      "name": "Ethereum Name Service",      "address": "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72",      "balance": 1144.0360760636715,      "balance_raw": "1144036076063671431479",      "usd_value": 6843.62,      "usd_price": 5.982    },    {      "chain": "ethereum",      "symbol": "WETH",      "name": "Wrapped Ether",      "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",      "balance": 1.461898164019089,      "balance_raw": "1461898164019088870",      "usd_value": 3689.2,      "usd_price": 2523.57    },    {      "chain": "ethereum",      "symbol": "WEI",      "name": "Wei Name Service",      "address": "0x0000000000696760e15f265e828db644a0c242eb",      "balance": 1.0,      "balance_raw": "1",      "usd_value": null,      "usd_price": null    }  ]}