> ## Documentation Index
> Fetch the complete documentation index at: https://developers.zerion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Zapper to Zerion API

> A 1:1 mapping from Zapper's GraphQL API to Zerion API, covering portfolio balances, DeFi positions, transaction history, NFTs, and token prices.

<Note>
  Zerion is offering discounts for teams migrating from Zapper. To claim yours, reach out via the chat widget on [dashboard.zerion.io](https://dashboard.zerion.io) or email [api@zerion.io](mailto:api@zerion.io).
</Note>

Zapper is [winding down all of its services](https://x.com/sebaudet26/status/2074918469376856150) on August 3, 2026, including the GraphQL API at build.zapper.xyz. If you've been querying `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](/supported-blockchains).

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`/`node` wrappers to unwrap.
* **One endpoint for tokens + DeFi:** Wallet tokens and DeFi positions both come from `/positions/`, switchable with `filter[positions]`. USD values are precomputed on every row.
* **Deeper Solana coverage:** Zapper covers token balances only on Solana ([no transaction history or price data](https://build.zapper.xyz/docs/api/supported-chains)). 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`](/api-reference/wallets/get-wallet-fungible-positions)                                          |
| DeFi positions               | `portfolioV2.appBalances`               | [`GET /v1/wallets/{address}/positions/?filter[positions]=only_complex`](/api-reference/wallets/get-wallet-fungible-positions)                                         |
| Tokens + DeFi (one call)     | `portfolioV2` with both sub-fields      | [`GET /v1/wallets/{address}/positions/?filter[positions]=no_filter`](/api-reference/wallets/get-wallet-fungible-positions)                                            |
| Portfolio total + 24h change | `totalBalanceUSD` per section           | [`GET /v1/wallets/{address}/portfolio`](/api-reference/wallets/get-wallet-portfolio)                                                                                  |
| Portfolio value over time    | Portfolio charts                        | [`GET /v1/wallets/{address}/charts/{period}`](/api-reference/wallets/get-wallet-balance-chart)                                                                        |
| Transaction history          | `transactionHistoryV2`                  | [`GET /v1/wallets/{address}/transactions/`](/api-reference/wallets/get-wallet-transactions)                                                                           |
| NFT holdings                 | `portfolioV2.nftBalances` + NFT queries | [`GET /v1/wallets/{address}/nft-positions/`](/api-reference/wallets/get-wallet-nft-positions) and [`/nft-portfolio`](/api-reference/wallets/get-wallet-nft-portfolio) |
| Token price + market data    | `fungibleTokenV2.priceData`             | [`GET /v1/fungibles/by-implementation`](/api-reference/fungibles/get-fungible-asset-by-implementation)                                                                |
| Price charts                 | `priceData.priceTicks`                  | [`GET /v1/fungibles/{id}/charts/{period}`](/api-reference/fungibles/get-a-chart-for-a-fungible-asset)                                                                 |
| Token search                 | `search`                                | [`GET /v1/fungibles/?filter[search_query]=…`](/api-reference/fungibles/get-list-of-fungible-assets)                                                                   |
| Realtime updates             | (polling)                               | [Transaction webhooks](/webhooks)                                                                                                                                     |

## Token balances

Zapper's `portfolioV2.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.

<CodeGroup>
  ```javascript JavaScript (EVM) theme={null}
  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"}`);
  }
  ```

  ```javascript JavaScript (Solana) theme={null}
  const API_KEY = process.env.ZERION_API_KEY;
  const address = "6sEk1enayZBGFyNvvJMTP7qs5S3uC7KLrQWaEk38hSHH";

  const res = await fetch(
    `https://api.zerion.io/v1/wallets/${address}/positions/?currency=usd&filter[chain_ids]=solana&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"}`);
  }
  ```

  ```python Python theme={null}
  import os, requests

  api_key = os.environ["ZERION_API_KEY"]
  address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

  res = requests.get(
      f"https://api.zerion.io/v1/wallets/{address}/positions/",
      params={
          "currency": "usd",
          "filter[positions]": "only_simple",
          "filter[trash]": "only_non_trash",
          "sort": "-value",
      },
      auth=(api_key, ""),
  )
  res.raise_for_status()

  for pos in res.json()["data"]:
      info = pos["attributes"]["fungible_info"]
      qty = pos["attributes"]["quantity"]["float"]
      value = pos["attributes"]["value"]
      chain = pos["relationships"]["chain"]["data"]["id"]
      print(f"{info['symbol']} on {chain}: {qty} = ${value:.2f}" if value else f"{info['symbol']} on {chain}: {qty}")
  ```

  ```bash cURL theme={null}
  curl -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_simple&filter[trash]=only_non_trash&sort=-value"
  ```
</CodeGroup>

### 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`](/api-reference/wallets/get-wallet-portfolio) → `positions_distribution_by_type.wallet` |

<Note>
  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](/spam-filtering).
</Note>

## Portfolio value

Zapper reports `totalBalanceUSD` separately on `tokenBalances`, `appBalances`, and `nftBalances`. Zerion's [`/portfolio`](/api-reference/wallets/get-wallet-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`](/api-reference/wallets/get-wallet-nft-portfolio).

```bash cURL theme={null}
curl -u "YOUR_API_KEY:" \
  "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/portfolio?currency=usd"
```

For a portfolio-value chart over time (Zapper's portfolio charts), use [`/wallets/{address}/charts/{period}`](/api-reference/wallets/get-wallet-balance-chart) with `hour`, `day`, `week`, `month`, `3months`, `6months`, `year`, `5years`, or `max`.

<Note>
  `/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.
</Note>

## DeFi positions

Zapper's `portfolioV2.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.

<CodeGroup>
  ```javascript JavaScript theme={null}
  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();

  // Rebuild Zapper's by-app grouping
  const byApp = {};
  for (const pos of data) {
    const app = pos.relationships.dapp?.data?.id ?? pos.attributes.protocol ?? "unknown";
    (byApp[app] ??= []).push(pos);
  }

  for (const [app, positions] of Object.entries(byApp)) {
    const total = positions.reduce((s, p) => s + (p.attributes.value ?? 0), 0);
    console.log(`${app}: $${total.toFixed(2)}`);
    for (const p of positions) {
      const { name, position_type, quantity, value } = p.attributes;
      console.log(`  [${position_type}] ${name}: ${quantity.float} = $${value?.toFixed(2) ?? "N/A"}`);
    }
  }
  ```

  ```python Python theme={null}
  import os, requests
  from collections import defaultdict

  api_key = os.environ["ZERION_API_KEY"]
  address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

  res = requests.get(
      f"https://api.zerion.io/v1/wallets/{address}/positions/",
      params={"currency": "usd", "filter[positions]": "only_complex", "sort": "-value"},
      auth=(api_key, ""),
  )

  by_app = defaultdict(list)
  for pos in res.json()["data"]:
      dapp = (pos["relationships"].get("dapp") or {}).get("data", {}).get("id")
      by_app[dapp or pos["attributes"].get("protocol") or "unknown"].append(pos)

  for app, positions in by_app.items():
      total = sum(p["attributes"]["value"] or 0 for p in positions)
      print(f"{app}: ${total:.2f}")
      for p in positions:
          a = p["attributes"]
          print(f"  [{a.get('position_type')}] {a['name']}: {a['quantity']['float']} = ${a['value'] or 0:.2f}")
  ```

  ```bash cURL theme={null}
  curl -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_complex&sort=-value"
  ```
</CodeGroup>

### 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}`](/api-reference/dapps/get-dapp-by-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`](/api-reference/wallets/get-wallet-portfolio) → `positions_distribution_by_type` (sum the non-wallet buckets) |

<Note>
  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`.
</Note>

## Transaction history

Zapper's `transactionHistoryV2` returns timeline events with a natural-language `interpretation.processedDescription` and per-account `deltas`. Zerion's [`/transactions/`](/api-reference/wallets/get-wallet-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.

<CodeGroup>
  ```javascript JavaScript (EVM) theme={null}
  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;

    // Equivalent of Zapper's processedDescription, composed from structured fields
    const parts = transfers.map((t) => {
      const sign = t.direction === "out" ? "-" : "+";
      const symbol = t.fungible_info?.symbol ?? "NFT";
      return `${sign}${t.quantity.float} ${symbol}`;
    });
    console.log(`[${mined_at}] ${operation_type} on ${chain}${dappId ? ` via ${dappId}` : ""}: ${parts.join(", ")}`);
    console.log(`  Fee: $${fee.value?.toFixed(2) ?? "?"}`);
  }
  ```

  ```javascript JavaScript (Solana) theme={null}
  const API_KEY = process.env.ZERION_API_KEY;
  const address = "6sEk1enayZBGFyNvvJMTP7qs5S3uC7KLrQWaEk38hSHH";
  const headers = {
    accept: "application/json",
    authorization: `Basic ${btoa(API_KEY + ":")}`,
  };

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

  for (const tx of data) {
    const { operation_type, mined_at, transfers, fee } = tx.attributes;
    const parts = transfers.map((t) => {
      const sign = t.direction === "out" ? "-" : "+";
      const symbol = t.fungible_info?.symbol ?? "NFT";
      return `${sign}${t.quantity.float} ${symbol}`;
    });
    console.log(`[${mined_at}] ${operation_type}: ${parts.join(", ")}`);
    console.log(`  Fee: $${fee.value?.toFixed(4) ?? "?"}`);
  }
  ```

  ```python Python theme={null}
  import os, requests

  api_key = os.environ["ZERION_API_KEY"]
  address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

  res = requests.get(
      f"https://api.zerion.io/v1/wallets/{address}/transactions/",
      params={"currency": "usd", "page[size]": 20},
      auth=(api_key, ""),
  )
  for tx in res.json()["data"]:
      attrs = tx["attributes"]
      chain = tx["relationships"]["chain"]["data"]["id"]
      dapp_id = (tx["relationships"].get("dapp") or {}).get("data", {}).get("id")
      parts = []
      for t in attrs["transfers"]:
          sign = "-" if t["direction"] == "out" else "+"
          symbol = (t.get("fungible_info") or {}).get("symbol", "NFT")
          parts.append(f"{sign}{t['quantity']['float']} {symbol}")
      via = f" via {dapp_id}" if dapp_id else ""
      print(f"[{attrs['mined_at']}] {attrs['operation_type']} on {chain}{via}: {', '.join(parts)}")
  ```

  ```bash cURL (EVM) theme={null}
  curl -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/transactions/?currency=usd&page[size]=20"
  ```

  ```bash cURL (Solana) theme={null}
  curl -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/wallets/6sEk1enayZBGFyNvvJMTP7qs5S3uC7KLrQWaEk38hSHH/transactions/?currency=usd&filter[chain_ids]=solana&page[size]=20"
  ```
</CodeGroup>

### 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](/supported-blockchains))                                                                |
| `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's `fungibleTokenV2(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](/api-reference/fungibles/get-a-chart-for-a-fungible-asset) for history.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const API_KEY = process.env.ZERION_API_KEY;
  const headers = {
    accept: "application/json",
    authorization: `Basic ${btoa(API_KEY + ":")}`,
  };

  // USDC on Ethereum
  const impl = "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";

  const res = await fetch(
    `https://api.zerion.io/v1/fungibles/by-implementation?implementation=${impl}&currency=usd`,
    { headers }
  );
  const { data } = await res.json();
  const { symbol, market_data } = data.attributes;
  console.log(`${symbol}: $${market_data.price} (24h: ${market_data.changes.percent_1d}%)`);

  // Historical chart (like priceTicks with timeFrame: DAY)
  const chartRes = await fetch(
    `https://api.zerion.io/v1/fungibles/${data.id}/charts/day?currency=usd`,
    { headers }
  );
  const { points } = (await chartRes.json()).data.attributes;
  // Each point is [unix_seconds, price]
  console.log(`${points.length} points, latest: $${points.at(-1)[1]}`);
  ```

  ```python Python theme={null}
  import os, requests

  api_key = os.environ["ZERION_API_KEY"]
  auth = (api_key, "")

  impl = "ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"  # USDC

  res = requests.get(
      "https://api.zerion.io/v1/fungibles/by-implementation",
      params={"implementation": impl, "currency": "usd"},
      auth=auth,
  )
  data = res.json()["data"]
  md = data["attributes"]["market_data"]
  print(f"{data['attributes']['symbol']}: ${md['price']} (24h: {md['changes']['percent_1d']}%)")

  chart = requests.get(
      f"https://api.zerion.io/v1/fungibles/{data['id']}/charts/day",
      params={"currency": "usd"},
      auth=auth,
  ).json()["data"]["attributes"]
  print(f"{len(chart['points'])} points, latest: ${chart['points'][-1][1]}")
  ```

  ```bash cURL theme={null}
  curl -u "YOUR_API_KEY:" \
    "https://api.zerion.io/v1/fungibles/by-implementation?implementation=ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&currency=usd"
  ```
</CodeGroup>

### 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`](/api-reference/fungibles/get-a-chart-for-a-fungible-asset) → `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…`](/api-reference/fungibles/get-list-of-fungible-assets) (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                                                                                                                                                                                 |

<Note>
  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`.
</Note>

## NFTs

Zapper's `nftBalances` totals map to [`/wallets/{address}/nft-portfolio`](/api-reference/wallets/get-wallet-nft-portfolio), and per-item holdings map to [`/wallets/{address}/nft-positions/`](/api-reference/wallets/get-wallet-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/`](/api-reference/wallets/get-wallet-nft-collections).

```bash cURL theme={null}
curl -u "YOUR_API_KEY:" \
  "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/nft-positions/?currency=usd&sort=-floor_price&page[size]=20"
```

Key fields per position: `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.

```javascript theme={null}
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;
}
```

## Webhooks (realtime updates)

Zapper's API has no push mechanism, so most integrations poll `transactionHistoryV2`. Zerion offers [transaction webhooks](/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](/recipes/wallet-activity-alerts) 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 `processedDescription` returns 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](#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 `search` spans tokens, NFTs, apps, and accounts. Zerion's search covers fungible tokens only, via [`/v1/fungibles/?filter[search_query]=…`](/api-reference/fungibles/get-list-of-fungible-assets).
* **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](/supported-blockchains) before migrating.

If any of these matter for your migration, [let us know](#get-in-touch). Your feedback helps shape our roadmap.

**Worth knowing:**

* **Authentication:** Zapper uses an `x-zapper-api-key` header on a GraphQL POST. Zerion uses [HTTP Basic Auth](/authentication) on REST GETs. Get a key at [dashboard.zerion.io](https://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 `chainId` values (and legacy network enums like `ETHEREUM_MAINNET`). Zerion uses string chain IDs (e.g. `ethereum`, `base`, `solana`). The [`/v1/chains/`](/api-reference/chains/get-list-of-all-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](https://jsonapi.org/). Payloads live under `data[].attributes` with related entities under `data[].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.id` client-side (see the [DeFi positions](#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`](/api-reference/fungibles/get-fungible-asset-by-implementation).
* **Currencies:** Zapper's `currency` argument on price ticks maps to Zerion's `currency` query parameter, which also applies to positions, portfolio, and transactions.

## 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](https://dashboard.zerion.io), or [email us](mailto:api@zerion.io).
