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

# Migrate from TopLedger

> A 1:1 mapping from the TopLedger API to the Octav API

A drop-in mapping from the **TopLedger Wallets API** to Octav. The biggest wins:

* **Solana *and* EVM.** TopLedger is Solana-only. The same Octav [`GET /v1/portfolio`](/api/endpoints/portfolio) accepts base58 Solana addresses **and** EVM addresses in the same shape.
* **Loose token holdings included.** TopLedger's `holdings` endpoint returns empty for most wallets. Octav returns your loose SOL/SPL tokens with per-asset detail (`symbol`, `balance`, `price`, `value`, `contract`).
* **Perps marked to market.** TopLedger values perps at deposited collateral and returns `pnl_usd` on the side. Octav folds unrealized PnL into net worth (collateral + PnL).
* **Per-asset detail, not aggregates.** TopLedger returns aggregate category values. Octav returns each asset inside every position.

<Info>
  **Get your API key** at [data.octav.fi](https://data.octav.fi/). Base URL: `https://api.octav.fi/v1`.
</Info>

## Endpoint mapping

| Use case            | TopLedger                                       | Octav                                                                       |
| ------------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
| Wallet tokens       | `GET /{wallet}/holdings`                        | [`GET /v1/portfolio`](/api/endpoints/portfolio) → `assetByProtocols.wallet` |
| DeFi positions      | `GET /{wallet}/analyze` → `categories`          | same `GET /v1/portfolio` → `assetByProtocols.<protocol>`                    |
| Net worth           | `GET /{wallet}/analyze` → `total_net_worth_usd` | same response → `networth` + `chains`                                       |
| Transaction history | *(not available)*                               | [`GET /v1/transactions`](/api/endpoints/transactions)                       |
| Chains covered      | Solana only                                     | Solana **and** EVM                                                          |

<Tip>
  Wallet tokens, DeFi positions, and net worth all come from a **single** `GET /v1/portfolio` call. There is no separate `analyze` and `holdings` request to merge.
</Tip>

## Authentication

TopLedger uses an `x-api-key` header. Octav uses a standard `Authorization: Bearer` token.

<CodeGroup>
  ```bash TopLedger theme={null}
  curl "https://api.topledger.xyz/api/wallets/9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM/analyze" \
    -H "x-api-key: YOUR_TOPLEDGER_KEY"
  ```

  ```bash Octav theme={null}
  curl "https://api.octav.fi/v1/portfolio?addresses=9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" \
    -H "Authorization: Bearer YOUR_OCTAV_KEY"
  ```
</CodeGroup>

## Wallet token balances

TopLedger's `holdings` endpoint returns `{ holdings: [], holdings_count }`, and in practice `holdings` is empty for most wallets, so loose SOL and SPL tokens go unreported. Octav returns them under the `wallet` protocol, grouped by chain, with per-asset detail:

```
assetByProtocols.wallet.chains.solana.protocolPositions.WALLET.assets[]
```

<CodeGroup>
  ```javascript JavaScript theme={null}
  const OCTAV_KEY = process.env.OCTAV_API_KEY;
  const address = "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM";

  const res = await fetch(
    `https://api.octav.fi/v1/portfolio?addresses=${address}&includeImages=true`,
    { headers: { Authorization: `Bearer ${OCTAV_KEY}` } }
  );
  const [portfolio] = await res.json(); // /v1/portfolio returns one entry per address

  // Flatten wallet tokens across every chain
  const wallet = portfolio.assetByProtocols.wallet;
  const tokens = Object.values(wallet.chains).flatMap((chain) =>
    Object.values(chain.protocolPositions).flatMap((pos) => pos.assets)
  );

  tokens.forEach((t) =>
    console.log(`${t.symbol} on ${t.chainKey}: ${t.balance} ($${t.value})`)
  );
  ```

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

  OCTAV_KEY = os.environ["OCTAV_API_KEY"]
  address = "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
  res = requests.get(
      "https://api.octav.fi/v1/portfolio",
      params={"addresses": address, "includeImages": True},
      headers={"Authorization": f"Bearer {OCTAV_KEY}"},
  )
  portfolio = res.json()[0]  # one entry per address

  wallet = portfolio["assetByProtocols"]["wallet"]
  for chain in wallet["chains"].values():
      for pos in chain["protocolPositions"].values():
          for t in pos["assets"]:
              print(f"{t['symbol']} on {t['chainKey']}: {t['balance']} (${t['value']})")
  ```

  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/portfolio?addresses=9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM&includeImages=true" \
    -H "Authorization: Bearer YOUR_OCTAV_KEY"
  ```
</CodeGroup>

**Field mapping**

| TopLedger (`categories.holdings`) | Octav (`…assets[]`)     | Notes                                                        |
| --------------------------------- | ----------------------- | ------------------------------------------------------------ |
| `value_usd`                       | sum of `assets[].value` | TopLedger gives one aggregate USD figure; Octav itemizes it. |
| `token_count`                     | `assets.length`         |                                                              |
| *(no per-token list)*             | `symbol`                | Per-token symbol, absent in TopLedger.                       |
| *(no per-token list)*             | `balance`               | Human-readable balance.                                      |
| *(no per-token list)*             | `price`                 | USD, precomputed.                                            |
| *(no per-token list)*             | `value`                 | USD value per token.                                         |
| *(no per-token list)*             | `contract`              | SPL mint address (native SOL uses the zero address).         |

## Net worth and per-chain breakdown

TopLedger's `total_net_worth_usd` maps onto the top-level `networth` returned by the same portfolio call. Octav also breaks the total down per chain and includes loose token holdings and marked-to-market perps that TopLedger omits.

| TopLedger (`analyze`) | Octav                                      | Notes                                                                             |
| --------------------- | ------------------------------------------ | --------------------------------------------------------------------------------- |
| `total_net_worth_usd` | `networth`                                 | Total net worth in USD. Octav also counts loose tokens and marks perps to market. |
| *(Solana only)*       | `chains.solana.value`                      | Per-chain total. EVM chains appear here too for multi-chain wallets.              |
| *(none)*              | `chains.<chain>.key` / `chainId`           | Chain identifier + numeric ID.                                                    |
| *(none)*              | `chains.<chain>.valuePercentile`           | Share of net worth per chain (Octav adds this).                                   |
| *(none)*              | `openPnl` / `closedPnl` / `totalCostBasis` | P\&L and cost basis (Octav adds these).                                           |

```javascript JavaScript theme={null}
console.log(`Net worth: $${portfolio.networth}`);
Object.values(portfolio.chains).forEach((c) =>
  console.log(`${c.name}: $${c.value} (${c.valuePercentile}%)`)
);
```

## DeFi positions

TopLedger returns decoded positions as **aggregate category values** under `categories` (lending, perpetuals, staking, rewards, and so on). Octav returns the same money under `assetByProtocols`, keyed by protocol, then chain, then position type (`LENDING`, `MARGIN`, `STAKED`, `LIQUIDITYPOOL`, `FARMING`, …), with each underlying asset itemized.

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Every protocol except the "wallet" bucket is a DeFi position
  const defi = Object.entries(portfolio.assetByProtocols).filter(
    ([key]) => key !== "wallet"
  );

  defi.forEach(([key, protocol]) => {
    console.log(`${protocol.name}: $${protocol.value}`);
    Object.values(protocol.chains).forEach((chain) => {
      Object.entries(chain.protocolPositions).forEach(([type, pos]) => {
        console.log(`  ${type}: $${pos.totalValue}`);
        pos.assets.forEach((a) => console.log(`    ${a.symbol}: $${a.value}`));
      });
    });
  });
  ```

  ```python Python theme={null}
  for key, protocol in portfolio["assetByProtocols"].items():
      if key == "wallet":
          continue
      print(f"{protocol['name']}: ${protocol['value']}")
      for chain in protocol["chains"].values():
          for ptype, pos in chain["protocolPositions"].items():
              print(f"  {ptype}: ${pos['totalValue']}")
  ```
</CodeGroup>

**Field mapping**

| TopLedger (`categories`)                                     | Octav                                  | Notes                                                              |
| ------------------------------------------------------------ | -------------------------------------- | ------------------------------------------------------------------ |
| `lending.protocols[].protocol`                               | `assetByProtocols.<key>.name`          | Keyed by protocol.                                                 |
| `lending.protocols[].net_value_usd`                          | `protocolPositions.LENDING.totalValue` | Net position value.                                                |
| `lending.protocols[].deposit_value_usd` / `borrow_value_usd` | `assets[]` (supplied / borrowed)       | Debt is decoded, not netted away.                                  |
| `perpetuals.protocols[]`                                     | `protocolPositions.MARGIN`             | `collateral_usd` + `pnl_usd` become one marked-to-market position. |
| `staking.protocols[]` (`token_symbol`, `staked_value_usd`)   | `protocolPositions.STAKED`             | Staked asset itemized in `assets[]`.                               |
| `rewards.protocols[].pending_rewards_usd`                    | `assets[]` (rewards)                   | Pending reward assets.                                             |
| `lp_positions` / `yield` / `governance`                      | matching `protocolPositions.<TYPE>`    | `LIQUIDITYPOOL`, `FARMING`, governance, etc.                       |
| *(all Solana)*                                               | `assetByProtocols.<key>.chains.solana` | EVM protocols also land here for multi-chain wallets.              |

<Tip>
  TopLedger reports perps at deposited collateral and hands you `pnl_usd` separately. Octav folds unrealized PnL into the `MARGIN` position and into `networth`, so the collateral + PnL figure is already marked to market.
</Tip>

## Transaction history

TopLedger has no wallet transaction endpoint. Octav adds [`GET /v1/transactions`](/api/endpoints/transactions), covering Solana **and** EVM.

<CodeGroup>
  ```bash TopLedger theme={null}
  # No wallet transaction endpoint available.
  ```

  ```bash Octav theme={null}
  curl "https://api.octav.fi/v1/transactions?addresses=9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" \
    -H "Authorization: Bearer YOUR_OCTAV_KEY"
  ```
</CodeGroup>

Octav categorizes each transaction (swap, deposit, stake, bridge, …) and prices transfers in USD. See the [transaction types reference](/api/reference/transaction-types).

## Key differences

* **Chains.** TopLedger is Solana-only. Octav covers Solana **and** EVM from the same endpoint and shape.
* **Loose tokens.** TopLedger's `holdings` endpoint returns empty for most wallets. Octav returns loose SOL/SPL tokens with per-asset detail in the `wallet` bucket.
* **Granularity.** TopLedger returns aggregate category values (`value_usd`, `token_count`). Octav returns each asset (`symbol`, `balance`, `price`, `value`) inside every position.
* **Perps.** TopLedger values perps at deposited collateral and reports `pnl_usd` separately. Octav marks perps to market (collateral + unrealized PnL) and includes them in `networth`.
* **Shape.** TopLedger groups by `categories` → `protocols[]`. Octav groups `assetByProtocols` → `chains` → `protocolPositions` → `assets[]`, with a dedicated `wallet` bucket for loose tokens.
* **Billing.** TopLedger charges roughly \$0.0004 per call. Octav charges **1 credit per call** (see [pricing](/api/pricing)).
* **P\&L.** Octav adds `openPnl`, `closedPnl`, and `totalCostBasis` at no extra call.

## Need help migrating?

<CardGroup cols={2}>
  <Card title="Join our Discord" icon="discord" href="https://discord.com/invite/qvcknAa73A">
    Share your TopLedger response shape and we'll map it.
  </Card>

  <Card title="Portfolio endpoint" icon="chart-pie" href="/api/endpoints/portfolio">
    Full reference for the endpoint you'll be calling.
  </Card>
</CardGroup>
