Skip to main content
If you’ve been calling Allium’s Realtime APIs for wallet balances, transactions, DeFi positions, PnL, or token prices, the same data is available on Zerion API across 60+ EVM chains and Solana, usually in a single call and with USD values precomputed. This guide shows the direct mapping for the main Allium Realtime endpoints, with copy-pasteable code for each. What you get with Zerion:
  • One address, all chains: Allium takes a {chain, address} pair per wallet, so a multi-chain balance read means one entry per chain. Zerion returns every supported chain for an address in one call to /wallets/{address}/positions/, filterable with filter[chain_ids].
  • One call for tokens + DeFi: Collapse Allium’s wallet/balances and wallet/positions into a single /positions/?filter[positions]=no_filter response.
  • Values precomputed: Allium balances return raw_balance (and a token.price you multiply yourself). Zerion returns quantity.float and value (USD) per position, plus a portfolio breakdown by chain and type.

Endpoint parity

Use caseAllium Realtime APIZerion API
Tokens + DeFi (one call)Two requests (wallet/balances + wallet/positions)GET /v1/wallets/{address}/positions/?filter[positions]=no_filter
Token balancesPOST /api/v1/developer/wallet/balancesGET /v1/wallets/{address}/positions/?filter[positions]=only_simple
DeFi positionsPOST /api/v1/developer/wallet/positionsGET /v1/wallets/{address}/positions/?filter[positions]=only_complex
Transactions / activityPOST /api/v1/developer/wallet/transactionsGET /v1/wallets/{address}/transactions/
Holdings PnLPOST /api/v1/developer/wallet/pnlGET /v1/wallets/{address}/pnl
Net worth + 24h change(compute from holdings)GET /v1/wallets/{address}/portfolio
Net worth over timewallet/balances/history (per-token snapshots)GET /v1/wallets/{address}/charts/{period}
Token pricePOST /api/v1/developer/pricesGET /v1/fungibles/by-implementation?implementation={chain}:{address}
Token metadata / searchtokens/list, tokens/searchGET /v1/fungibles/
NFTs(historical NFT trades)GET /v1/wallets/{address}/nft-positions/
Multiple wallets in one callArray body (1-100 wallets)Wallet sets (/v1/wallet-sets/...)
Realtime updatesStreamingTransaction webhooks
Prefer not to write code? The Zerion CLI wraps the same endpoints with a one-shot npx @zerion/cli init flow, useful for quick experiments and AI agents.

A note on request shape

The biggest structural change is how you address a wallet:
  • Allium: POST an array of {chain, address} objects. You pick the chain per wallet, and you can batch up to 100 wallets in one request.
  • Zerion: GET /v1/wallets/{address}/... with the address in the path. One address per call, all supported chains by default, narrowed with filter[chain_ids]. To read many wallets in one request, use the wallet sets endpoints.
So a single Allium call that fans out across [{ethereum, 0x…}, {polygon, 0x…}] for one wallet becomes a single Zerion call to that address (both chains already included), and a single Allium call across several different wallets becomes either N Zerion calls or one wallet-set call.

Token balances

Allium’s wallet/balances returns one item per token with raw_balance (an integer) and a token object that carries price but no precomputed USD value. Zerion returns a JSON:API collection where each token is one entry under data[], with attributes.fungible_info for metadata, attributes.quantity.float for the decimal-adjusted amount, and attributes.value for the USD value already computed. The same endpoint accepts both EVM and Solana addresses.
const API_KEY = process.env.ZERION_API_KEY;
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const res = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/positions/?currency=usd&filter[positions]=only_simple&filter[trash]=only_non_trash&sort=-value`,
  {
    headers: {
      accept: "application/json",
      authorization: `Basic ${btoa(API_KEY + ":")}`,
    },
  }
);
const { data } = await res.json();

for (const pos of data) {
  const { fungible_info, quantity, price, value } = pos.attributes;
  const chain = pos.relationships.chain.data.id;
  console.log(`${fungible_info.symbol} on ${chain}: ${quantity.float} @ $${price} = $${value?.toFixed(2) ?? "N/A"}`);
}

Field mapping

Allium (items[].…)Zerion (data[].attributes.…)
token.info.symbol, token.info.namefungible_info.symbol, fungible_info.name
token.decimalsfungible_info.implementations[].decimals
token.address (native type for gas tokens)fungible_info.implementations[].address (null for native)
chainrelationships.chain.data.id
raw_balance / raw_balance_str (integer, not decimal-adjusted)quantity.int (raw integer string). Also quantity.float (decimal number) and quantity.numeric (decimal string), so you don’t divide by 10^decimals yourself.
token.priceprice
raw_balance / 10^decimals × token.price (compute client-side)value (USD, precomputed)
token.attributes.total_liquidity_usd (with with_liquidity_info=true)No direct equivalent. Use filter[trash] for spam, and fungible_info.flags.verified for the curation signal.
token.attributes.price_diff_pct_1dAvailable per asset via /v1/fungibles/{id}market_data.changes.percent_1d
block_timestampupdated_at (ISO 8601, last balance update)
(no equivalent)fungible_info.icon.url (token logo, returned by default)

Transactions

Allium’s wallet/transactions returns transactions with asset_transfers[] (each tagged sent/received), a labels[] array, and an activities[] array describing what the transaction did (dex_trade, nft_trade, asset_approval, and so on). Zerion’s /transactions/ returns enriched, human-readable transactions with a single decoded operation_type, inlined transfer metadata, fees, and the dApp when Zerion recognizes it. The same endpoint accepts both EVM and Solana addresses.
const API_KEY = process.env.ZERION_API_KEY;
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const headers = {
  accept: "application/json",
  authorization: `Basic ${btoa(API_KEY + ":")}`,
};

const res = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/transactions/?currency=usd&page[size]=20`,
  { headers }
);
const { data } = await res.json();

for (const tx of data) {
  const { operation_type, mined_at, transfers, fee } = tx.attributes;
  const chain = tx.relationships.chain.data.id;
  const dappId = tx.relationships.dapp?.data?.id;

  console.log(`[${mined_at}] ${operation_type} on ${chain}`);
  if (dappId) console.log(`  via ${dappId}`);
  for (const t of transfers) {
    const sign = t.direction === "out" ? "-" : "+";
    const symbol = t.fungible_info?.symbol ?? "NFT";
    console.log(`  ${sign}${t.quantity.float} ${symbol} ($${t.value?.toFixed(2) ?? "?"})`);
  }
  console.log(`  Fee: $${fee.value?.toFixed(2) ?? "?"}`);
}

Field mapping

Allium (items[].…)Zerion (data[].attributes.…)
hashhash
block_timestampmined_at (ISO 8601) / mined_at_block
chainrelationships.chain.data.id
labels[] (coarse tags, e.g. transfer) + activities[].type (richer DeFi actions: dex_trade, nft_trade, asset_approval, asset_bridge, …)operation_type (trade, send, receive, approve, revoke, deposit, withdraw, mint, burn, claim, bid, delegate, revoke_delegation, execute, deploy). Zerion folds Allium’s classification into one decoded field; activities[] is empty for plain transfers, where the tag sits in labels[].
type (numeric EVM envelope type, e.g. 2 for EIP-1559)No equivalent. This is the raw transaction type, not a semantic category; Zerion does not expose it.
asset_transfers[].transfer_type (sent / received)transfers[].direction (out / in)
asset_transfers[].asset.type (native, evm_erc20, evm_erc721, evm_erc1155)transfers[].fungible_info vs transfers[].nft_info
asset_transfers[].asset.symbol, .decimalstransfers[].fungible_info.symbol, .implementations[].decimals
asset_transfers[].amount.raw_amount / .amounttransfers[].quantity.int (raw) / .float (decimal). USD in transfers[].value.
asset_transfers[].from_address / .to_addresstransfers[].sender / transfers[].recipient
from_address / to_address (transaction-level)sent_from / sent_to
fee.amount / fee.raw_amountfee.value (USD) / fee.quantity.float
(no equivalent)relationships.dapp.data.id (dApp slug, e.g. uniswap-v3, present when Zerion identifies it)

Filter mapping

Allium paramZerion equivalent
activity_type=dex_tradefilter[operation_types]=trade (comma-separated for multiple)
transaction_hash=0x…filter[hash]=0x…
limit=100page[size]=100
cursor=<cursor>Follow links.next from the response
(chain set per wallet in the body)filter[chain_ids]=ethereum,base

DeFi positions

Allium’s wallet/positions returns typed positions, where the field set depends on position_type (LP, lending, staked, regular, perp, vault). Lending positions nest supplies[], borrows[], and collateral[]; LP positions carry token0/token1; staked positions carry staked_token and rewards_token. Zerion flattens all of this: each position is one row under /positions/?filter[positions]=only_complex, tagged with protocol, protocol_module, and position_type (including loan for borrowed assets).
const API_KEY = process.env.ZERION_API_KEY;
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const res = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/positions/?currency=usd&filter[positions]=only_complex&sort=-value`,
  { headers: { accept: "application/json", authorization: `Basic ${btoa(API_KEY + ":")}` } }
);
const { data } = await res.json();

for (const pos of data) {
  const { name, protocol, protocol_module, position_type, quantity, value } = pos.attributes;
  const chain = pos.relationships.chain.data.id;
  console.log(`[${position_type}] ${name} | ${protocol} (${protocol_module}) on ${chain}: ${quantity.float} = $${value?.toFixed(2) ?? "N/A"}`);
}

Field mapping

Allium (items[].…)Zerion (data[].attributes.…)
protocolprotocol (plus relationships.dapp.data.id for the slug)
position_type (LP, lending, staked, perp, vault)protocol_module (lending, staked, liquidity_pool, locked, rewards, vesting, deposit, investment, yield) + position_type (deposit, loan, staked, reward, locked)
total_value_usdvalue (per row). Wallet-level totals are in /portfoliopositions_distribution_by_type.
supplies[] / collateral[]rows with position_type: deposit / staked
borrows[]rows with position_type: loan
staked_amount, unclaimed_rewardsrows with position_type: staked / reward
token0 / token1 (LP pair)one row per pool token, grouped by group_id (see positions)
health_factorNo direct equivalent. Derive from supplied vs borrowed value.
apy, fee_tier, in_range, unclaimed_fees_*No direct equivalent. Zerion surfaces the position’s underlying token, USD value, and protocol module.
chainrelationships.chain.data.id
(no equivalent)application_metadata.name / application_metadata.icon.url (protocol display name + logo)
Allium returns dedicated perp positions (size, entry/mark price, leverage, liquidation price, unrealized PnL). Zerion’s positions model is spot and DeFi-protocol oriented and does not return perpetuals as positions. If perps matter for your migration, let us know.

Holdings PnL

Allium’s wallet/pnl returns per-token PnL (realized_pnl, unrealized_pnl, average_cost) plus wallet totals. Zerion’s /pnl returns wallet-level PnL computed with FIFO: realized and unrealized gain, net invested, total fees, and external in/out flows.
const API_KEY = process.env.ZERION_API_KEY;
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const res = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/pnl?currency=usd`,
  { headers: { accept: "application/json", authorization: `Basic ${btoa(API_KEY + ":")}` } }
);
const { data } = await res.json();
const a = data.attributes;

console.log(`Realized gain:   $${a.realized_gain?.toFixed(2)}`);
console.log(`Unrealized gain: $${a.unrealized_gain?.toFixed(2)}`);
console.log(`Net invested:    $${a.net_invested?.toFixed(2)}`);
console.log(`Total fees:      $${a.total_fee?.toFixed(2)}`);

Field mapping

Allium (items[].…)Zerion (data.attributes.…)
total_realized_pnl.amountrealized_gain
total_unrealized_pnl.amountunrealized_gain
total_balance.amountCurrent value via /portfoliototal.positions
tokens[].average_costNo wallet-summary equivalent. Per-token cost basis is reflected in net_invested / realized_cost_basis.
tokens[].realized_pnl / .unrealized_pnl (per token)Wallet-level realized_gain / unrealized_gain. For a per-token breakdown, filter with filter[fungible_ids] or filter[fungible_implementations].
min_liquidity (query)No direct equivalent. Assets without prices are excluded automatically and reported in meta.
(no equivalent)net_invested, total_fee, received_external, sent_external
Allium’s wallet/pnl accepts a batch of wallets and returns per-token rows. To replicate a per-token breakdown on Zerion, call /pnl with filter[fungible_ids]=…. See the wallet PnL tracker recipe for a worked example.

Token prices

Allium’s prices endpoint takes a batch of {token_address, chain} objects and returns the latest price with OHLC. Zerion resolves a token by its implementation and returns full asset metadata including market_data.price and recent changes.
const API_KEY = process.env.ZERION_API_KEY;
const implementation = "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; // USDC

const res = await fetch(
  `https://api.zerion.io/v1/fungibles/by-implementation?implementation=${implementation}&currency=usd`,
  { headers: { accept: "application/json", authorization: `Basic ${btoa(API_KEY + ":")}` } }
);
const { data } = await res.json();
const m = data.attributes.market_data;

console.log(`${data.attributes.symbol}: $${m.price}`);
console.log(`24h change: ${m.changes?.percent_1d?.toFixed(2)}%`);

Field mapping

Allium (items[].…)Zerion (data.attributes.…)
pricemarket_data.price
open / high / low / closeNo direct equivalent for the latest interval. Use the fungible chart for a price timeseries.
timestampmarket_data.price is the live value; chart points carry timestamps.
decimalsimplementations[].decimals
(one request per implementation)One implementation per call. For a list, page /v1/fungibles/ with filter[implementation_address].
Allium’s prices endpoint batches up to 200 tokens per request. Zerion’s by-implementation resolves one token per call. For a batch, query /v1/fungibles/ and read market_data.price from each result, or cache the Zerion fungible_id per token and reuse it.

Pagination

Replace Allium’s cursor query parameter with Zerion’s links.next URL. Each Zerion response includes a fully-formed next-page link you can fetch as-is.
async function getAll(url) {
  const all = [];
  const headers = { accept: "application/json", authorization: `Basic ${btoa(API_KEY + ":")}` };

  while (url) {
    const res = await fetch(url, { headers });
    const { data, links } = await res.json();
    all.push(...data);
    url = links?.next ?? null;
  }
  return all;
}

Realtime updates

Allium offers a streaming product for low-latency blockchain data. Zerion offers transaction webhooks: subscribe a callback URL to one or more wallets and receive a POST when any of them transact. See the wallet activity alerts recipe for a working example.

Differences from Allium

Most Allium Realtime use cases have a direct Zerion equivalent. A few aren’t covered, and others behave differently. Worth a scan before you cut over. Not supported today:
  • Non-EVM, non-Solana chains: Allium Realtime covers chains like Bitcoin, Stellar, NEAR, and Sui. Zerion covers EVM chains and Solana. Check the supported chains list for the ones you rely on.
  • Perpetuals: Allium returns perp positions with leverage, mark price, and liquidation price. Zerion’s positions model does not return perpetuals.
  • Per-token historical balance snapshots: Allium’s wallet/balances/history returns point-in-time token balances. Zerion exposes wallet value over time via /charts/{period} and asset price history via the fungible chart, but not a per-token balance-at-timestamp endpoint.
  • OHLC price candles: Allium’s prices endpoint returns open/high/low/close per interval. Zerion returns live price plus a chart timeseries, not OHLC candles.
  • Liquidity and holder analytics: Allium exposes total_liquidity_usd and holders_count on tokens. Zerion does not return pool liquidity or holder counts.
If any of these matter for your migration, let us know. Your feedback helps shape our roadmap. Worth knowing:
  • Authentication: Allium uses an X-API-KEY header. Zerion uses HTTP Basic Auth. Get a key at dashboard.zerion.io.
  • Request shape: Allium endpoints are POST with a JSON array of {chain, address} objects. Zerion endpoints are GET /v1/wallets/{address}/... with the address in the path. See the note above.
  • One address spans all chains: Allium pairs each address with one chain. Zerion returns every supported chain for an address by default, narrowed with filter[chain_ids].
  • Multiple wallets: Allium batches up to 100 wallets per call. Zerion reads one address per /wallets/ call; for batches use the wallet sets endpoints.
  • Values precomputed: Allium balances return raw_balance and a token.price; you divide by 10^decimals and multiply. Zerion returns quantity.float and value (USD) per position.
  • One endpoint for tokens and DeFi: Zerion serves both wallet tokens and DeFi positions from /positions/. Switch via filter[positions]=only_simple (wallet only), only_complex (DeFi only), or no_filter (both).
  • Flattened DeFi: Allium returns typed positions with nested supplies/borrows/collateral. Zerion returns one row per position tagged with protocol_module and position_type (including loan for debt). Group by relationships.dapp.data.id to reconstruct protocols, and by group_id to reconstruct LP pairs.
  • Chain IDs: Allium and Zerion both use lowercase string chain IDs (e.g. ethereum, solana), so most map 1:1. Confirm the longer-tail names against the full list.
  • Response shape: Zerion uses JSON:API. Payloads live under data[].attributes with related entities under data[].relationships.
  • Spam filtering: Allium gates dust with min_liquidity / with_liquidity_info. Zerion uses filter[trash]=only_non_trash. See spam filtering for the full taxonomy.
  • Pagination: Allium pages with a cursor query parameter; Zerion returns a fully-formed links.next URL you can fetch as-is. See pagination.

Get in touch

Have a use case we don’t cover or need assistance with the migration? Our team is happy to help! Reach out via the chat widget on dashboard.zerion.io, or email us.