> ## 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 using the Zerion API.

**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 to any LLM)

## Steps

<Steps>
  ### 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,
      })),
    }));
  }
  ```

  ### 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,
  };
  ```

  ### 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);
  ```

  ### 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);
  ```
</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)
