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

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

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

* **One call instead of two.** Nansen splits a portfolio across `POST /profiler/address/current-balance` (tokens) and `POST /portfolio/defi-holdings` (DeFi). Octav returns wallet tokens, DeFi positions, and net worth from a single [`GET /v1/portfolio`](/api/endpoints/portfolio).
* **No double-counting.** Merge Nansen's two endpoints naively and a protocol receipt token (an aToken, say) shows up in both `current-balance` and inside `defi-holdings`, inflating the total. Octav decodes each asset once, so the numbers add up.
* **One GET, not two POSTs.** Nansen wants JSON bodies on POST requests and paginates balances. Octav returns everything in one GET with no page loop.
* **Logos and P\&L included.** Nansen returns no token logos. Octav returns asset logos with `includeImages=true`, plus `openPnl`, `closedPnl`, and `totalCostBasis` in the same response.

<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                   | Nansen                                   | Octav                                                                       |
| -------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- |
| Wallet tokens (all chains) | `POST /profiler/address/current-balance` | [`GET /v1/portfolio`](/api/endpoints/portfolio) → `assetByProtocols.wallet` |
| DeFi positions             | `POST /portfolio/defi-holdings`          | same `GET /v1/portfolio` → `assetByProtocols.<protocol>`                    |
| Net worth                  | sum of both responses                    | same response → `networth` + `chains`                                       |
| Transaction history        | —                                        | [`GET /v1/transactions`](/api/endpoints/transactions)                       |
| Token metadata / price     | inline on `current-balance` rows         | inline on every asset (`price`, `value`, `decimal`)                         |

<Tip>
  Everything comes from a **single** `GET /v1/portfolio` call. There is no separate DeFi request to merge, and no pagination loop to unwind.
</Tip>

## Authentication

Nansen uses an `apikey` header and POSTs a JSON body. Octav uses a standard `Authorization: Bearer` token on a GET.

<CodeGroup>
  ```bash Nansen theme={null}
  curl -X POST "https://api.nansen.ai/api/v1/profiler/address/current-balance" \
    -H "apikey: YOUR_NANSEN_KEY" \
    -H "content-type: application/json" \
    -d '{"chain":"all","address":"0x6426af179aabebe47666f345d69fd9079673f6cd","hide_spam_token":true,"pagination":{"page":1,"per_page":100}}'
  ```

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

## Wallet token balances

Nansen's `current-balance` returns a paginated `data` array, one row per token. In Octav, wallet tokens live under the `wallet` protocol, grouped by chain, and arrive in full with no page loop:

```
assetByProtocols.wallet.chains.<chain>.protocolPositions.WALLET.assets[]
```

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

  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 — no pagination to unwind
  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 = "0x6426af179aabebe47666f345d69fd9079673f6cd"
  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=0x6426af179aabebe47666f345d69fd9079673f6cd&includeImages=true" \
    -H "Authorization: Bearer YOUR_OCTAV_KEY"
  ```
</CodeGroup>

**Field mapping**

| Nansen (`current-balance` `data[]`) | Octav (`…assets[]`)                    | Notes                                                                             |
| ----------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------- |
| `token_address`                     | `contract`                             | Token address. Native tokens use the zero address in Octav.                       |
| `chain`                             | `chainKey`                             | e.g. `eth` → `ethereum`. See [supported chains](/api/reference/supported-chains). |
| `token_symbol`                      | `symbol`                               |                                                                                   |
| `token_name`                        | `name`                                 |                                                                                   |
| `token_amount`                      | `balance`                              | Human-readable in both.                                                           |
| `price_usd`                         | `price`                                | USD, precomputed.                                                                 |
| `value_usd`                         | `value`                                | Precomputed in both.                                                              |
| —                                   | asset logo (with `includeImages=true`) | Nansen returns no logos; Octav adds them.                                         |
| `hide_spam_token` flag              | —                                      | Octav applies its own spam filtering; there is no flag to pass.                   |

<Tip>
  Nansen paginates `current-balance` and you loop pages until `pagination.is_last_page` is `true`. Octav returns every token in one response, so there is no page loop.
</Tip>

## Net worth and per-chain breakdown

To get net worth from Nansen you sum the `value_usd` across `current-balance` plus the `total_value_usd` across `defi-holdings` — which is exactly where the double-count creeps in. Octav returns net worth directly on the top-level `networth` field, with each asset counted once, plus a per-chain breakdown under `chains`.

| Nansen                                            | Octav                                      | Notes                                           |
| ------------------------------------------------- | ------------------------------------------ | ----------------------------------------------- |
| sum of `current-balance` + `defi-holdings` values | `networth`                                 | Octav counts each asset once — no double-count. |
| per-chain sum (compute yourself)                  | `chains.<chain>.value`                     | Per-chain total.                                |
| `chain` string                                    | `chains.<chain>.key` / `chainId`           | Chain identifier + numeric ID.                  |
| —                                                 | `chains.<chain>.valuePercentile`           | Share of net worth per chain (Octav adds this). |
| —                                                 | `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

Nansen returns decoded positions under `defi-holdings.protocols[]`, each with a `tokens[]` array tagged by `position_type`. Octav returns the same money under `assetByProtocols`, keyed by protocol, then chain, then position type (`LENDING`, `LIQUIDITYPOOL`, `STAKED`, `FARMING`, …).

<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**

| Nansen (`defi-holdings.protocols[]`) | Octav                                         | Notes                           |
| ------------------------------------ | --------------------------------------------- | ------------------------------- |
| `protocol_name`                      | `assetByProtocols.<key>.name`                 | Keyed by protocol.              |
| `total_value_usd`                    | `protocolPositions.<TYPE>.totalValue`         | Net position value.             |
| `tokens[].position_type`             | asset role (supply / borrow / stake / reward) | Drives the position `TYPE` key. |
| `tokens[].symbol`                    | asset `symbol`                                |                                 |
| `tokens[].amount`                    | asset `balance`                               |                                 |
| `tokens[].value_usd`                 | asset `value`                                 |                                 |
| `chain`                              | `assetByProtocols.<key>.chains.<chain>`       |                                 |

<Tip>
  Because Octav decodes each asset once, a protocol receipt token never appears in both the wallet bucket and a DeFi position. That is the double-count you had to reconcile when merging Nansen's two endpoints.
</Tip>

## Transaction history

Nansen has no dedicated portfolio transaction feed to migrate from. Octav provides one at [`GET /v1/transactions`](/api/endpoints/transactions).

<CodeGroup>
  ```bash Octav theme={null}
  curl "https://api.octav.fi/v1/transactions?addresses=0x6426af179aabebe47666f345d69fd9079673f6cd" \
    -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

* **Calls per portfolio.** Nansen: `current-balance` (paginated) + `defi-holdings`. Octav: one `GET /v1/portfolio`.
* **Double-counting.** Merging Nansen's two endpoints double-counts protocol receipt tokens. Octav decodes each asset once, so `networth` is correct out of the box.
* **HTTP shape.** Nansen uses POST with JSON bodies and page loops. Octav uses a single GET with query params.
* **Chains.** Both cover EVM and Solana. Octav serves them from the same endpoint and response shape.
* **Grouping.** Nansen groups DeFi by `protocols[]` → `tokens[]`. Octav groups `assetByProtocols` → `chains` → `protocolPositions` → `assets[]`, with a dedicated `wallet` bucket for loose tokens.
* **Logos.** Nansen returns no token logos. Octav returns them with `includeImages=true`.
* **Billing.** Nansen bills on credits with a subscription (one of the priciest). 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 Nansen 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>
