Skip to main content
What you’ll build:
  • Fetch all DeFi positions (staked, deposited, LP, locked, rewards)
  • Filter by position type and chain
  • Group positions by protocol and calculate per-protocol totals
  • Build a complete runnable DeFi dashboard
Position                  | Protocol     | Module         | Amount    | Value
Supplied USDC             | Aave V3      | lending        | 5,000.00  | $5,000.00
Staked ETH                | Lido         | staked         | 1.20      | $2,543.88
ETH/USDC LP              | Uniswap V3   | liquidity_pool | 0.85      | $1,801.15
Locked CRV                | Curve        | locked         | 10,000.00 | $450.00
Unclaimed ARB             | Arbitrum     | rewards        | 200.00    | $230.00
Time: ~10 minutes

Prerequisites

Steps

1
Fetch DeFi positions
2
Call the positions endpoint with filter[positions]=only_complex to get only DeFi protocol positions, excluding regular wallet tokens.
3
JavaScript
const API_KEY = process.env.ZERION_API_KEY;
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const headers = {
  accept: "application/json",
  authorization: `Basic ${btoa(API_KEY + ":")}`,
};

const response = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/positions/?currency=usd&filter[positions]=only_complex&sort=-value`,
  { headers }
);
if (!response.ok) throw new Error(`API error: ${response.status}`);

const { data } = await response.json();

for (const position of data) {
  const { name, protocol, protocol_module, quantity, value } = position.attributes;
  console.log(
    `${name} | ${protocol} (${protocol_module}) | ${quantity?.float} | ${value != null ? `$${value.toFixed(2)}` : "N/A"}`
  );
}
Python
import os, requests

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

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

for position in response.json()["data"]:
    attrs = position["attributes"]
    print(
        f"{attrs['name']} | "
        f"{attrs.get('protocol', 'N/A')} ({attrs.get('protocol_module', 'N/A')}) | "
        f"{attrs['quantity']['float']} | "
        f"{'${:.2f}'.format(attrs['value']) if attrs.get('value') is not None else 'N/A'}"
    )
cURL
curl -u "YOUR_API_KEY:" \
  "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_complex&sort=-value"
4
Key parameters: filter[positions]=only_complex (DeFi positions only), sort=-value (highest value first), currency=usd (also supports eur, gbp, etc.)
5
Filter by position type (optional)
6
Use filter[position_types] to narrow results to specific DeFi categories.
7
JavaScript
const response = await fetch(
  `https://api.zerion.io/v1/wallets/${address}/positions/?currency=usd&filter[positions]=only_complex&filter[position_types]=deposit,staked`,
  { headers }
);

const { data } = await response.json();
Python
response = requests.get(
    f"https://api.zerion.io/v1/wallets/{address}/positions/",
    params={
        "currency": "usd",
        "filter[positions]": "only_complex",
        "filter[position_types]": "deposit,staked",
    },
    auth=(api_key, ""),
)
cURL
curl -u "YOUR_API_KEY:" \
  "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_complex&filter[position_types]=deposit,staked"
8
Available position types:
9
TypeDescriptiondepositAssets deposited into lending pools, vaults, or liquidity poolsstakedAssets staked for rewards, governance, or consensusrewardUnclaimed rewards earned from protocolslockedVote-escrowed or time-locked positionswalletRegular wallet assets (excluded by only_complex)
10
Filter by chain (optional)
11
To get DeFi positions on specific chains only, add filter[chain_ids].
12
curl -u "YOUR_API_KEY:" \
  "https://api.zerion.io/v1/wallets/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/positions/?currency=usd&filter[positions]=only_complex&filter[chain_ids]=ethereum,arbitrum"
13
Full working example
14
Save as defi-dashboard.mjs and run with node defi-dashboard.mjs:
15
const API_KEY = process.env.ZERION_API_KEY;
const BASE_URL = "https://api.zerion.io/v1";
const headers = {
  accept: "application/json",
  authorization: `Basic ${btoa(API_KEY + ":")}`,
};

async function getAllDeFiPositions(address) {
  const allPositions = [];
  let url = `${BASE_URL}/wallets/${address}/positions/?currency=usd&filter[positions]=only_complex&sort=-value`;

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

  return allPositions;
}

async function displayDeFiDashboard(address) {
  const positions = await getAllDeFiPositions(address);

  // Group by protocol
  const byProtocol = {};
  for (const pos of positions) {
    const protocol = pos.attributes.protocol || "Unknown";
    if (!byProtocol[protocol]) byProtocol[protocol] = { total: 0, positions: [] };
    byProtocol[protocol].total += pos.attributes.value || 0;
    byProtocol[protocol].positions.push(pos);
  }

  // Sort protocols by total value
  const sorted = Object.entries(byProtocol).sort((a, b) => b[1].total - a[1].total);

  const grandTotal = positions.reduce((sum, p) => sum + (p.attributes.value || 0), 0);
  console.log(`=== DeFi OVERVIEW ===`);
  console.log(`Total DeFi value: $${grandTotal.toFixed(2)}`);
  console.log(`Protocols: ${sorted.length}`);
  console.log(`Positions: ${positions.length}\n`);

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

displayDeFiDashboard("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");

Response fields reference

Each DeFi position includes:
FieldDescription
nameHuman-readable position name
protocolProtocol name (e.g., “Aave V3”, “Lido”)
protocol_moduleType of DeFi interaction: lending, staked, liquidity_pool, locked, rewards, farming, vesting, deposit
position_typePosition category (deposit, staked, reward, locked)
quantity.floatToken amount
valueUSD value of the position
group_idID to group related positions (e.g., both sides of an LP)
relationships.chainWhich blockchain the position is on

Next steps

  • Use filter[positions]=no_filter to get both DeFi and wallet positions in a single call
  • Combine with the portfolio endpoint to see total value distribution by type
  • Implement pagination to handle wallets with many DeFi positions