Zerion is offering discounts for teams migrating from Zapper. To claim yours, reach out via the chat widget on dashboard.zerion.io or email api@zerion.io.
portfolioV2 for token, app, and NFT balances, or transactionHistoryV2 for wallet activity, the same data is available on Zerion API across 60+ EVM chains and Solana.
This guide shows the direct mapping for the main Zapper queries, with copy-pasteable code for each.
What you get with Zerion:
- Plain REST: Each use case is a single GET request with query-string filters. No GraphQL documents to maintain, no
edges/nodewrappers to unwrap. - One endpoint for tokens + DeFi: Wallet tokens and DeFi positions both come from
/positions/, switchable withfilter[positions]. USD values are precomputed on every row. - Deeper Solana coverage: Zapper covers token balances only on Solana (no transaction history or price data). Zerion returns Solana balances, enriched transaction history, and price charts from the same endpoints as EVM.
- Realtime webhooks: Subscribe wallets once and receive a POST when they transact, instead of polling transaction history.
Endpoint parity
| Use case | Zapper GraphQL | Zerion API |
|---|---|---|
| Token balances | portfolioV2.tokenBalances | GET /v1/wallets/{address}/positions/?filter[positions]=only_simple |
| DeFi positions | portfolioV2.appBalances | GET /v1/wallets/{address}/positions/?filter[positions]=only_complex |
| Tokens + DeFi (one call) | portfolioV2 with both sub-fields | GET /v1/wallets/{address}/positions/?filter[positions]=no_filter |
| Portfolio total + 24h change | totalBalanceUSD per section | GET /v1/wallets/{address}/portfolio |
| Portfolio value over time | Portfolio charts | GET /v1/wallets/{address}/charts/{period} |
| Transaction history | transactionHistoryV2 | GET /v1/wallets/{address}/transactions/ |
| NFT holdings | portfolioV2.nftBalances + NFT queries | GET /v1/wallets/{address}/nft-positions/ and /nft-portfolio |
| Token price + market data | fungibleTokenV2.priceData | GET /v1/fungibles/by-implementation |
| Price charts | priceData.priceTicks | GET /v1/fungibles/{id}/charts/{period} |
| Token search | search | GET /v1/fungibles/?filter[search_query]=… |
| Realtime updates | (polling) | Transaction webhooks |
Token balances
Zapper’sportfolioV2.tokenBalances.byToken returns a Relay-style connection of token nodes. Zerion returns a flat JSON:API collection where each token is one entry under data[], with attributes.fungible_info for metadata and attributes.value for the USD value. The same endpoint accepts both EVM and Solana addresses.
Field mapping
Zapper (byToken.edges[].node.…) | Zerion (data[].attributes.…) |
|---|---|
symbol, name | fungible_info.symbol, fungible_info.name |
tokenAddress (0x0000…0000 for native) | fungible_info.implementations[].address (null for native) |
decimals | fungible_info.implementations[].decimals |
balance (decimal number) | quantity.float (also quantity.numeric as decimal string) |
balanceRaw (raw integer string) | quantity.int (1:1, raw integer string) |
balanceUSD | value |
price | price |
imgUrlV2 | fungible_info.icon.url |
network (object with name, chainId) | relationships.chain.data.id (string ID, e.g. "ethereum") |
byToken(first: 25) + cursor paging | Not needed; /positions/ returns all positions in one response |
tokenBalances.totalBalanceUSD | /portfolio → positions_distribution_by_type.wallet |
Zapper’s
byToken accepts a minBalanceUSD argument to cut dust by value. Zerion’s lever is a spam classifier instead: pass filter[trash]=only_non_trash to hide spam tokens, and filter client-side on value if you also want a hard USD threshold. See spam filtering.Portfolio value
Zapper reportstotalBalanceUSD separately on tokenBalances, appBalances, and nftBalances. Zerion’s /portfolio returns the fungible total with a 24h change, a breakdown by chain, and a breakdown by position type (wallet, deposited, borrowed, locked, staked) in one response. The NFT total lives on /nft-portfolio.
cURL
/wallets/{address}/charts/{period} with hour, day, week, month, 3months, 6months, year, 5years, or max.
/portfolio totals count only positions with flags.displayable: true. Zerion marks some wallet-held DeFi derivative tokens (LP tokens, vault shares, index tokens) as non-displayable, so the wallet bucket can be lower than a raw sum over /positions/. If you want a Zapper-style total that counts every priced token, sum value over the /positions/ rows instead.DeFi positions
Zapper’sportfolioV2.appBalances groups positions by app, with nested positionBalances that are either app tokens (LP tokens, vault shares) or contract positions with underlying tokens[]. Zerion returns one flat row per position from the same /positions/ endpoint with filter[positions]=only_complex; group by relationships.dapp.data.id client-side if you want the per-app view back.
Field mapping
Zapper (appBalances.byApp.edges[].node.…) | Zerion (data[].attributes.…) |
|---|---|
app.displayName, app.imgUrl | application_metadata.name, application_metadata.icon.url |
app.slug | relationships.dapp.data.id (look up details via /v1/dapps/{id}) |
network | relationships.chain.data.id |
balanceUSD (per app) | Sum value over rows grouped by relationships.dapp.data.id (see code above) |
positionBalances[].groupLabel | protocol_module (lending, staked, liquidity_pool, locked, rewards, vesting, deposit, investment, yield) |
App token node (symbol, balance, balanceUSD, price) | fungible_info, quantity, value, price |
tokens[].metaType (SUPPLIED, CLAIMABLE, BORROWED, LOCKED, VESTING) | position_type (deposit, reward, loan, locked, staked) |
displayProps.label | name |
appBalances.totalBalanceUSD | /portfolio → positions_distribution_by_type (sum the non-wallet buckets) |
Zapper nests every underlying token of a contract position under
tokens[]. Zerion returns one row per position with a single fungible_info; a two-sided LP shows up as its pool position with the position’s total USD value.Transaction history
Zapper’stransactionHistoryV2 returns timeline events with a natural-language interpretation.processedDescription and per-account deltas. Zerion’s /transactions/ returns structured, enriched transactions instead: an operation_type, typed transfers[] with direction and USD values, the fee, and the dApp when Zerion recognizes it. Compose display strings from those fields.
The same endpoint accepts both EVM and Solana addresses. Unlike Zapper’s transactionHistoryV2, which returns no Solana activity, Zerion returns the same enriched shape (trades, sends, USD values, fees) for Solana wallets.
Field mapping
Zapper (transactionHistoryV2.edges[].node.…) | Zerion (data[].attributes.…) |
|---|---|
transaction.hash | hash |
transaction.timestamp (milliseconds) | mined_at (ISO 8601) / mined_at_block |
transaction.network (enum, e.g. ETHEREUM_MAINNET) | relationships.chain.data.id (string, e.g. "ethereum") |
interpretation.processedDescription | No prose equivalent. Compose from operation_type (send, receive, trade, approve, deposit, withdraw, mint, burn, claim, execute, deploy) + transfers[] (see code above) |
interpretation.descriptionDisplayItems | transfers[].fungible_info / transfers[].nft_info |
deltas → tokenDeltasV2 amount sign | transfers[].direction (in/out) + transfers[].quantity |
token.symbol, token.decimals | transfers[].fungible_info.symbol, .implementations[].decimals |
methodSignature | acts[].application_metadata.method.name (decoded method name, when available) |
| (no Zapper equivalent) | transfers[].value (USD value per transfer) |
| (no Zapper equivalent) | fee.value, fee.fungible_info |
| (no Zapper equivalent) | relationships.dapp.data.id (dApp slug, e.g. "uniswap-v3") |
Filter mapping
| Zapper argument | Zerion equivalent |
|---|---|
first: 20 | page[size]=20 |
after: <cursor> | Follow links.next from the response |
filters: { chainIds: [1, 8453] } | filter[chain_ids]=ethereum,base (string IDs, see the full list) |
perspective: Signer / Receiver / All | No direct parameter. Zerion returns all transactions involving the wallet; filter client-side on sent_from / sent_to if you need the signer-only view. |
| (no Zapper equivalent) | filter[operation_types]=trade,send, filter[asset_types]=fungible,nft, filter[min_mined_at] / filter[max_mined_at] |
Token prices and charts
Zapper’sfungibleTokenV2(address, chainId) returns market data and OHLC priceTicks. On Zerion, look the asset up by implementation (chain:address), read market_data for the price and stats, and call the charts endpoint for history.
Field mapping
Zapper (fungibleTokenV2.…) | Zerion (data.attributes.…) |
|---|---|
address + chainId (query args) | implementation=<chain>:<address> (string chain ID) |
symbol, name, decimals, imageUrlV2 | symbol, name, implementations[].decimals, icon.url |
priceData.price | market_data.price |
priceData.priceChange24h | market_data.changes.percent_1d (also percent_30d, percent_90d, percent_365d) |
priceData.marketCap | market_data.market_cap |
priceData.volume24h | market_data.trading_volumes.volume_1d |
priceData.priceTicks(timeFrame: DAY) | /charts/day → points as [unix_seconds, price] pairs, plus stats (first, min, avg, max, last) |
fungibleTokenBatchV2 (multiple tokens) | /v1/fungibles/?filter[fungible_implementations]=ethereum:0xa0b8…,ethereum:0xdac1… (one call, comma-separated chain:address pairs) |
priceChange5m, priceChange1h | Not available; shortest change window is 1 day. Compute from /charts/hour points if you need finer granularity. |
totalLiquidity, totalGasTokenLiquidity | No equivalent |
Zapper chart timestamps are in milliseconds; Zerion chart points use Unix seconds. Chart periods map as HOUR →
hour, DAY → day, WEEK → week, MONTH → month, YEAR → year, plus 3months, 6months, 5years, and max.NFTs
Zapper’snftBalances totals map to /wallets/{address}/nft-portfolio, and per-item holdings map to /wallets/{address}/nft-positions/, which returns each NFT with collection metadata, media URLs, and a floor-price-based value. For a collection-level rollup like Zapper’s NFT collection queries, use /wallets/{address}/nft-collections/.
cURL
nft_info.contract_address, nft_info.token_id, nft_info.name, nft_info.content.preview.url for media, and price/value for the floor-based valuation. value is null when Zerion has no floor data for the collection, so handle missing values when totaling (Zapper’s nftBalances.totalBalanceUSD includes estimates for more collections, so totals can differ). The NFT endpoints accept EVM addresses only.
Pagination
Replace Zapper’s Relay cursors (first/after + pageInfo.endCursor) with Zerion’s links.next URL. Paginated endpoints (transactions, NFT positions, fungibles) include a fully-formed next-page link you can fetch as-is. /positions/ is not paginated and returns the full set in one response.
Webhooks (realtime updates)
Zapper’s API has no push mechanism, so most integrations polltransactionHistoryV2. 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 Zapper
Most Zapper 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:- Onchain identity: Zapper resolves ENS, Basenames, Farcaster, and Lens profiles. Zerion doesn’t; resolve names to addresses with an ENS library or a provider like Neynar before calling Zerion.
- Farcaster portfolios: Zapper accepts Farcaster usernames/FIDs as portfolio subjects. On Zerion, resolve the user’s verified addresses first, then pass those.
- Natural-language descriptions: Zapper’s
processedDescriptionreturns prose like “Swapped 400 USDC for 0.2 ETH”. Zerion returns structured fields (operation_type,transfers[],fee); compose your own template (see the transaction history section). - Token holders and rankings: Zapper exposes holder lists and trending-token rankings. Not on Zerion today; pair with a separate data source if you need them.
- Cross-entity search: Zapper’s
searchspans tokens, NFTs, apps, and accounts. Zerion’s search covers fungible tokens only, via/v1/fungibles/?filter[search_query]=…. - Bitcoin and other non-EVM chains: Zapper lists Bitcoin and a few other non-EVM networks. Zerion covers 60+ EVM chains and Solana; check the supported chains list before migrating.
- Authentication: Zapper uses an
x-zapper-api-keyheader on a GraphQL POST. Zerion uses HTTP Basic Auth on REST GETs. Get a key at dashboard.zerion.io. - Solana: Zapper supports Solana for token balances only. Zerion additionally returns enriched transaction history and price charts for Solana, on the same endpoints as EVM. Solana DeFi positions and NFTs are not yet supported (the NFT endpoints accept EVM addresses only).
- Chain identifiers: Zapper uses numeric
chainIdvalues (and legacy network enums likeETHEREUM_MAINNET). Zerion uses string chain IDs (e.g.ethereum,base,solana). The/v1/chains/endpoint lists them all with their numeric counterparts. - Timestamps: Zapper returns millisecond epochs. Zerion returns ISO 8601 strings on transactions (
mined_at) and Unix seconds in chart points. - Response shape: Zerion uses JSON:API. Payloads live under
data[].attributeswith related entities underdata[].relationships, no GraphQL field selection. - Per-app grouping: Zapper groups DeFi by app with nested position balances. Zerion returns one flat row per position; group by
relationships.dapp.data.idclient-side (see the DeFi positions section). - Token identifiers: Zapper keys tokens by
chainId+ contract address. Zerion uses its own chain-agnostic fungible IDs; resolve one via/v1/fungibles/by-implementation. - Currencies: Zapper’s
currencyargument on price ticks maps to Zerion’scurrencyquery parameter, which also applies to positions, portfolio, and transactions.