Portfolio Data
Access complete onchain portfolio data for any Farcaster user by resolving their addresses through via their FID (Farcaster ID) or username, then using those addresses to fetch their portfolio data.
Overview
This process involves two steps:
- Query the farcasterProfileendpoint to get connected addresses
- Use the resolved addresses to query the portfolioV2endpoint
Typical Usage
A code sample is provided at the end of this page, implementing the following suggested setup.
Step 1: Resolve Farcaster Addresses
First, query the farcasterProfile endpoint using either FIDs or Farcaster usernames to get associated addresses.
Example Variables
{
  "fids": [99], // Query by FID
  "farcasterUsernames": ["jessepollak"] // Or query by username
}
Example farcasterProfile Query
query GetFarcasterProfile($username: String) {
  farcasterProfile(username: $username) {
    username
    fid
    metadata {
      displayName
      description
      imageUrl
      warpcast
    }
    custodyAddress
    connectedAddresses
  }
}
Example Response
{
  "data": {
    "farcasterProfile": {
      "username": "jessepollak",
      "fid": 99,
      "metadata": {
        "displayName": "jesse.base.eth 🔵",
        "description": "@base builder #001; onchain cities w/ OAK & city3",
        "imageUrl": "https://imagedelivery.net/BXluQx4ige9GuW0Ia56BHw/1013b0f6-1bf4-4f4e-15fb-34be06fede00/original",
        "warpcast": "https://warpcast.com/jessepollak"
      },
      "custodyAddress": "0x4ce34af3378a00c640125e4dbf4c9e64dff4c93b",
      "connectedAddresses": [
        "0x2211d1d0020daea8039e46cf1367962070d77da9",
        "0x6adea326faea1b688af33df59e18f7a819bcaa4f",
        "0x6e0d9c6dd8a08509bb625caa35dc61a991406f62",
        "0x849151d7d0bf1f34b70d5cad5149d28cc2308bf1",
        "0x85c0ba9e1456bc755a6ce69e1a85ccaa1faa9e41",
        "0x8e86e5331d3a020909c9e42ea9051675555f5e49",
        "0xe73f9c181b571cac2bf3173634d04a9921b7ffcf"
      ]
    }
  }
}
Step 2: Fetch Portfolio Data
Using the connectedAddresses and custodyAddress obtained from Step 1, query the portfolioV2 endpoint to get a complete onchain portfolio. Access rich data across tokenBalances, appBalances, nftBalances, totals, and claimables.
Combine the connectedAddresses and custodyAddress into a single array for the portfolio query:
Example Portfolio Variables
{
  "addresses": [
    // Connected addresses
    "0x6adea326faea1b688af33df59e18f7a819bcaa4f",
    "0x8e86e5331d3a020909c9e42ea9051675555f5e49",
    "0x2211d1d0020daea8039e46cf1367962070d77da9",
    "0x6e0d9c6dd8a08509bb625caa35dc61a991406f62",
    "0xe73f9c181b571cac2bf3173634d04a9921b7ffcf",
    "0x849151d7d0bf1f34b70d5cad5149d28cc2308bf1",
    // Custody address
    "0x4ce34af3378a00c640125e4dbf4c9e64dff4c93b"
  ]
}
Example Portfolio (tokenBalances) Query
query TokenBalances($addresses: [Address!]!, $first: Int) {
  portfolioV2(addresses: $addresses) {
    tokenBalances {
      totalBalanceUSD
      byToken(first: $first) {
        totalCount
        edges {
          node {
            symbol
            tokenAddress
            balance
            balanceUSD
            price
            imgUrlV2
            name
            network {
              name
            }
          }
        }
      }
    }
  }
}
Example Response
{
  "data": {
    "portfolioV2": {
      "tokenBalances": {
        "totalBalanceUSD": 292328.8286857288,
        "byToken": {
          "totalCount": 1223,
          "edges": [
            {
              "node": {
                "symbol": "ETH",
                "tokenAddress": "0x0000000000000000000000000000000000000000",
                "balance": 17.286417826281596,
                "balanceUSD": 45317.035788310255,
                "price": 2621.54,
                "imgUrlV2": "https://storage.googleapis.com/zapper-fi-assets/tokens/base/0x0000000000000000000000000000000000000000.png",
                "name": "Ethereum",
                "network": {
                  "name": "Base"
                }
              }
            },
            {
              "node": {
                "symbol": "SYNDOG",
                "tokenAddress": "0x3d1d651761d535df881740ab50ba4bd8a2ec2c00",
                "balance": 30000000,
                "balanceUSD": 38615.1,
                "price": 0.00128717,
                "imgUrlV2": "https://storage.googleapis.com/zapper-fi-assets/tokens/base/0x3d1d651761d535df881740ab50ba4bd8a2ec2c00.png",
                "name": "Synthesizer Dog",
                "network": {
                  "name": "Base"
                }
              }
            },
            {
              "node": {
                "symbol": "BKIT",
                "tokenAddress": "0x262a9f4e84efa2816d87a68606bb4c1ea3874bf1",
                "balance": 28980487535.238518,
                "balanceUSD": 28654.60195290476,
                "price": 9.88755e-7,
                "imgUrlV2": "https://storage.googleapis.com/zapper-fi-assets/tokens/base/0x262a9f4e84efa2816d87a68606bb4c1ea3874bf1.png",
                "name": "Bangkit",
                "network": {
                  "name": "Base"
                }
              }
            },
            {
              "node": {
                "symbol": "ERA",
                "tokenAddress": "0xb82d1d9dd2ca8cfbdecdfbfef0aaebb07eb29312",
                "balance": 50408655.54694379,
                "balanceUSD": 13756.164212247695,
                "price": 0.0002728929002963999,
                "imgUrlV2": "https://storage.googleapis.com/zapper-fi-assets/tokens/base/0xb82d1d9dd2ca8cfbdecdfbfef0aaebb07eb29312.png",
                "name": "BASE ERA",
                "network": {
                  "name": "Base"
                }
              }
            },
            {
              "node": {
                "symbol": "BNKR",
                "tokenAddress": "0x22af33fe49fd1fa80c7149773dde5890d3c76f3b",
                "balance": 55169518.28804873,
                "balanceUSD": 10414.901662417838,
                "price": 0.00018878,
                "imgUrlV2": "https://storage.googleapis.com/zapper-fi-assets/tokens/base/0x22af33fe49fd1fa80c7149773dde5890d3c76f3b.png",
                "name": "BankrCoin",
                "network": {
                  "name": "Base"
                }
              }
            }
          ]
        }
      }
    }
  }
}
Key Fields
Farcaster Profile Fields
| Field | Description | Type | 
|---|---|---|
| username | Farcaster username | String! | 
| fid | Farcaster ID | Int! | 
| connectedAddresses | Array of associated addresses | [String!]! | 
| custodyAddress | Address that owns the farcaster account. | [String!]! | 
| metadata | Profile metadata | FarcasterMetadata! | 
Metadata Fields
| Field | Description | Type | 
|---|---|---|
| displayName | User's display name | String | 
| description | User's profile description/bio | String | 
| imageUrl | URL of the user's profile image | String | 
| warpcast | User's Warpcast profile URL | String | 
Portfolio Fields
Inherits all fields from the standard portfolio query, including:
- Token balances
- App positions
- NFT holdings
- Portfolio totals
Remember to handle cases where a Farcaster user might have multiple connected addresses. You should aggregate portfolio data across all addresses for a complete view.
Error Handling
Common scenarios to handle:
- Invalid or non-existent Farcaster username/FID
- Farcaster profile with no connected addresses
Best Practices
- Cache resolved Farcaster addresses to minimize API calls
- Use batch queries when fetching portfolios for multiple Farcaster users
- Implement proper error handling for invalid or non-existent Farcaster profiles
- Consider implementing pagination for users with large portfolios
Code Example (React)
Following is an example of displaying portfolio data for a farcaster FID, and a farcaster username.
import { ApolloClient, InMemoryCache, createHttpLink, gql, useQuery, ApolloProvider } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
// Types for the GraphQL responses
interface BaseToken {
  name: string;
  symbol: string;
}
interface Token {
  balance: string;
  balanceUSD: number;
  baseToken: BaseToken;
}
interface TokenBalance {
  address: string;
  network: string;
  token: Token;
}
interface PortfolioData {
  portfolio: {
    tokenBalances: TokenBalance[];
  };
}
interface AccountsData {
  accounts: {
    address: string;
  }[];
}
// Set up Apollo Client
const httpLink = createHttpLink({
  uri: 'https://public.zapper.xyz/graphql',
});
const API_KEY = 'YOUR_API_KEY';
const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      'x-zapper-api-key': API_KEY,
    },
  };
});
const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});
const AccountsQuery = gql`
  query($fids: [Float!], $farcasterUsernames: [String!]) {
    accounts(fids: $fids, farcasterUsernames: $farcasterUsernames) {
      address
    }
  }
`;
const PortfolioQuery = gql`
  query providerPorfolioQuery($addresses: [Address!]!, $networks: [Network!]!) {
    portfolio(addresses: $addresses, networks: $networks) {
      tokenBalances {
        address
        network
        token {
          balance
          balanceUSD
          baseToken {
            name
            symbol
          }
        }
      }
    }
  }
`;
function Portfolio() {
  // First, fetch the addresses
  const { loading: addressesLoading, data: addressesData } = useQuery<AccountsData>(AccountsQuery, {
    variables: {
      fids: [177],
      farcasterUsernames: ['jasper'],
    },
  });
  // Then use the addresses to fetch portfolio data
  const { loading: portfolioLoading, data: portfolioData } = useQuery<PortfolioData>(PortfolioQuery, {
    variables: {
      addresses: addressesData?.accounts.map((account) => account.address) || [],
      networks: ['ETHEREUM_MAINNET'],
    },
    skip: !addressesData,
  });
  // Handle loading states
  if (addressesLoading || portfolioLoading) return <div>Loading...</div>;
  // Handle no data
  if (!addressesData || !portfolioData) return <div>No data found</div>;
  return (
    <div className="p-4">
      <h2 className="text-xl font-bold mb-4">Portfolio Data for {addressesData.accounts.length} Addresses</h2>
      {portfolioData.portfolio.tokenBalances.map((balance, index) => (
        <div key={`${balance.address}-${index}`} className="mb-4 p-4 border rounded shadow-sm">
          <p className="font-semibold">Token: {balance.token.baseToken.name}</p>
          <p>Symbol: {balance.token.baseToken.symbol}</p>
          <p>Balance: {balance.token.balance}</p>
          <p>Value (USD): ${balance.token.balanceUSD.toFixed(2)}</p>
          <p>Network: {balance.network}</p>
          <p className="text-sm text-gray-600">Address: {balance.address}</p>
        </div>
      ))}
    </div>
  );
}
function App() {
  return (
    <ApolloProvider client={client}>
      <Portfolio />
    </ApolloProvider>
  );
}
export default App;