> ## 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.

# Build an AI agent with onchain data

> Give your AI agent access to wallet portfolios, token prices, and transaction history through Zerion API tool calls with a step-by-step OpenAI-style example.

**What you'll build:**

* Tool functions for portfolio lookup, token search, and transaction history
* A working agent using the OpenAI SDK that answers wallet questions
* Patterns for connecting Zerion data to any AI framework

**Time:** \~15 minutes

## Prerequisites

* A Zerion API key ([get one here](https://dashboard.zerion.io))
* Node.js 18+
* An OpenAI API key (for the agent example; you can adapt it to any LLM)

## Steps

<Steps>
  <Step title="Define the Zerion tool functions">
    Create reusable functions that your agent can call. Each function maps to a Zerion API endpoint: [wallet portfolio](/api-reference/wallets/get-wallet-portfolio), [wallet positions](/api-reference/wallets/get-wallet-fungible-positions), [fungible assets search](/api-reference/fungibles/get-list-of-fungible-assets), and [wallet transactions](/api-reference/wallets/get-wallet-transactions).

    ```javascript theme={null}
    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 + ":")}`,
    };

    // Tool 1: Get wallet portfolio summary
    async function getWalletPortfolio(address) {
      const res = await fetch(
        `${BASE_URL}/wallets/${address}/portfolio?currency=usd`,
        { headers }
      );
      const { data } = await res.json();
      const attrs = data.attributes;
      return {
        total_value: attrs.total.positions,
        change_24h_percent: attrs.changes.percent_1d,
        change_24h_usd: attrs.changes.absolute_1d,
        chains: attrs.positions_distribution_by_chain,
      };
    }

    // Tool 2: Get top token positions
    async function getWalletPositions(address, limit = 10) {
      const res = await fetch(
        `${BASE_URL}/wallets/${address}/positions/?currency=usd&sort=value&filter[positions]=only_simple`,
        { headers }
      );
      const { data } = await res.json();
      return data.slice(0, limit).map((pos) => ({
        symbol: pos.attributes.fungible_info.symbol,
        name: pos.attributes.fungible_info.name,
        chain: pos.relationships.chain.data.id,
        quantity: pos.attributes.quantity.float,
        value_usd: pos.attributes.value,
        price: pos.attributes.price,
      }));
    }

    // Tool 3: Search for a token by name
    async function searchToken(query) {
      const res = await fetch(
        `${BASE_URL}/fungibles/?currency=usd&filter[search_query]=${encodeURIComponent(query)}&sort=-market_data.market_cap&page[size]=5`,
        { headers }
      );
      const { data } = await res.json();
      return data.map((token) => ({
        id: token.id,
        symbol: token.attributes.symbol,
        name: token.attributes.name,
        price: token.attributes.market_data?.price,
        market_cap: token.attributes.market_data?.market_cap,
      }));
    }

    // Tool 4: Get recent transactions
    async function getRecentTransactions(address, limit = 5) {
      const res = await fetch(
        `${BASE_URL}/wallets/${address}/transactions/?currency=usd&page[size]=${limit}`,
        { headers }
      );
      const { data } = await res.json();
      return data.map((tx) => ({
        type: tx.attributes.operation_type,
        timestamp: tx.attributes.mined_at,
        chain: tx.relationships.chain.data.id,
        transfers: tx.attributes.transfers?.map((t) => ({
          direction: t.direction,
          symbol: t.fungible_info?.symbol || "NFT",
          quantity: t.quantity?.float,
          value_usd: t.value,
        })),
      }));
    }
    ```
  </Step>

  <Step title="Wire up the agent with tool definitions">
    Define the tools schema so the LLM knows what functions are available and what parameters they accept.

    ```javascript theme={null}
    import OpenAI from "openai";

    const openai = new OpenAI();

    const tools = [
      {
        type: "function",
        function: {
          name: "getWalletPortfolio",
          description: "Get total portfolio value, 24h change, and chain breakdown for a wallet address",
          parameters: {
            type: "object",
            properties: {
              address: { type: "string", description: "Wallet address (0x... or ENS)" },
            },
            required: ["address"],
          },
        },
      },
      {
        type: "function",
        function: {
          name: "getWalletPositions",
          description: "Get the top token holdings for a wallet, sorted by USD value",
          parameters: {
            type: "object",
            properties: {
              address: { type: "string", description: "Wallet address" },
              limit: { type: "number", description: "Max tokens to return (default 10)" },
            },
            required: ["address"],
          },
        },
      },
      {
        type: "function",
        function: {
          name: "searchToken",
          description: "Search for a token by name or symbol and get its price and market cap",
          parameters: {
            type: "object",
            properties: {
              query: { type: "string", description: "Token name or symbol to search for" },
            },
            required: ["query"],
          },
        },
      },
      {
        type: "function",
        function: {
          name: "getRecentTransactions",
          description: "Get recent transactions for a wallet with transfer details",
          parameters: {
            type: "object",
            properties: {
              address: { type: "string", description: "Wallet address" },
              limit: { type: "number", description: "Max transactions to return (default 5)" },
            },
            required: ["address"],
          },
        },
      },
    ];

    // Map function names to implementations
    const toolFunctions = {
      getWalletPortfolio,
      getWalletPositions,
      searchToken,
      getRecentTransactions,
    };
    ```
  </Step>

  <Step title="Run the agent loop">
    Process user messages, call tools when the LLM requests them, and return the results.

    ```javascript theme={null}
    async function chat(userMessage) {
      const messages = [
        {
          role: "system",
          content: "You are a helpful onchain assistant. Use the available tools to look up wallet data, token prices, and transactions. Always cite specific numbers from the API.",
        },
        { role: "user", content: userMessage },
      ];

      while (true) {
        const response = await openai.chat.completions.create({
          model: "gpt-4o", // or any model that supports tool calling
          messages,
          tools,
        });

        const message = response.choices[0].message;
        messages.push(message);

        // If no tool calls, return the final answer
        if (!message.tool_calls?.length) {
          return message.content;
        }

        // Execute each tool call and append results
        for (const toolCall of message.tool_calls) {
          const fn = toolFunctions[toolCall.function.name];
          const args = JSON.parse(toolCall.function.arguments);
          const result = await fn(...Object.values(args));

          messages.push({
            role: "tool",
            tool_call_id: toolCall.id,
            content: JSON.stringify(result),
          });
        }
      }
    }

    // Try it
    const answer = await chat("What tokens does vitalik.eth hold? What's the total value?");
    console.log(answer);
    ```
  </Step>

  <Step title="Full working example">
    Save as `agent.mjs` and run with `node agent.mjs`:

    ```javascript theme={null}
    import OpenAI from "openai";

    const ZERION_API_KEY = process.env.ZERION_API_KEY;
    const BASE_URL = "https://api.zerion.io/v1";
    const headers = {
      accept: "application/json",
      authorization: `Basic ${btoa(ZERION_API_KEY + ":")}`,
    };

    async function getWalletPortfolio(address) {
      const res = await fetch(`${BASE_URL}/wallets/${address}/portfolio?currency=usd`, { headers });
      const { data } = await res.json();
      const a = data.attributes;
      return { total_value: a.total.positions, change_24h_percent: a.changes.percent_1d, chains: a.positions_distribution_by_chain };
    }

    async function getWalletPositions(address, limit = 10) {
      const res = await fetch(`${BASE_URL}/wallets/${address}/positions/?currency=usd&sort=value&filter[positions]=only_simple`, { headers });
      const { data } = await res.json();
      return data.slice(0, limit).map((p) => ({
        symbol: p.attributes.fungible_info.symbol, chain: p.relationships.chain.data.id,
        quantity: p.attributes.quantity.float, value_usd: p.attributes.value,
      }));
    }

    async function searchToken(query) {
      const res = await fetch(`${BASE_URL}/fungibles/?currency=usd&filter[search_query]=${encodeURIComponent(query)}&sort=-market_data.market_cap&page[size]=5`, { headers });
      const { data } = await res.json();
      return data.map((t) => ({ symbol: t.attributes.symbol, name: t.attributes.name, price: t.attributes.market_data?.price }));
    }

    async function getRecentTransactions(address, limit = 5) {
      const res = await fetch(`${BASE_URL}/wallets/${address}/transactions/?currency=usd&page[size]=${limit}`, { headers });
      const { data } = await res.json();
      return data.map((tx) => ({
        type: tx.attributes.operation_type, timestamp: tx.attributes.mined_at,
        chain: tx.relationships.chain.data.id,
        transfers: tx.attributes.transfers?.map((t) => ({ direction: t.direction, symbol: t.fungible_info?.symbol || "NFT", quantity: t.quantity?.float })),
      }));
    }

    const toolFunctions = { getWalletPortfolio, getWalletPositions, searchToken, getRecentTransactions };

    const tools = [
      { type: "function", function: { name: "getWalletPortfolio", description: "Get portfolio value and chain breakdown", parameters: { type: "object", properties: { address: { type: "string" } }, required: ["address"] } } },
      { type: "function", function: { name: "getWalletPositions", description: "Get top token holdings by value", parameters: { type: "object", properties: { address: { type: "string" }, limit: { type: "number" } }, required: ["address"] } } },
      { type: "function", function: { name: "searchToken", description: "Search tokens by name/symbol", parameters: { type: "object", properties: { query: { type: "string" } }, required: ["query"] } } },
      { type: "function", function: { name: "getRecentTransactions", description: "Get recent wallet transactions", parameters: { type: "object", properties: { address: { type: "string" }, limit: { type: "number" } }, required: ["address"] } } },
    ];

    const openai = new OpenAI();

    async function chat(userMessage) {
      const messages = [
        { role: "system", content: "You are a helpful onchain data assistant. Use tools to answer questions about wallets, tokens, and transactions." },
        { role: "user", content: userMessage },
      ];

      while (true) {
        const res = await openai.chat.completions.create({ model: "gpt-4o", messages, tools });
        const msg = res.choices[0].message;
        messages.push(msg);

        if (!msg.tool_calls?.length) return msg.content;

        for (const tc of msg.tool_calls) {
          const fn = toolFunctions[tc.function.name];
          const args = JSON.parse(tc.function.arguments);
          const result = await fn(...Object.values(args));
          messages.push({ role: "tool", tool_call_id: tc.id, content: JSON.stringify(result) });
        }
      }
    }

    const answer = await chat("What's in vitalik.eth's wallet? Give me a summary.");
    console.log(answer);
    ```
  </Step>
</Steps>

## Adapting to other frameworks

The tool functions work with any AI framework. Here's how to connect them:

**Anthropic Claude (tool\_use)**

```javascript theme={null}
const response = await anthropic.messages.create({
  model: "claude-sonnet-4-20250514",
  messages: [{ role: "user", content: userMessage }],
  tools: tools.map((t) => ({
    name: t.function.name,
    description: t.function.description,
    input_schema: t.function.parameters,
  })),
});
```

**LangChain**

```javascript theme={null}
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const portfolioTool = tool(
  async ({ address }) => JSON.stringify(await getWalletPortfolio(address)),
  {
    name: "getWalletPortfolio",
    description: "Get portfolio value and chain breakdown",
    schema: z.object({ address: z.string() }),
  }
);
```

**Vercel AI SDK**

```javascript theme={null}
import { tool } from "ai";
import { z } from "zod";

const walletTool = tool({
  description: "Get portfolio value and chain breakdown",
  parameters: z.object({ address: z.string() }),
  execute: async ({ address }) => getWalletPortfolio(address),
});
```

## Next steps

* Connect via [MCP](/build-with-ai/mcp) to give AI tools access to the full Zerion docs and API spec
* Add [PnL tracking](/recipes/wallet-pnl-tracker) tools for cost basis and gain/loss analysis
* Use [webhooks](/recipes/wallet-activity-alerts) to push new transactions to your agent in real time
* Explore [x402](/build-with-ai/x402) for per-request stablecoin payments (no API keys needed)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.