---
title: "Wallet API"
description: "用 REST 查代币持有人排行、地址持仓和成交，与 On-chain DEX API 共用 key、配额和定价"
url: "https://blockreq.com/docs/zh-cn/build/wallet-api/"
---

# Wallet API

**Wallet API** 用 REST 回答钱包相关的问题：谁持有某个代币、一个地址持有什么以及值多少美元、哪些地址在某个池子里交易过。按仓位的盈亏和平均成本[即将开放](#即将开放仓位与盈亏)。

Wallet API 和 [On-chain DEX API](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md) 是 **Crypto Data API** 下的两个产品，除了产品页，其余全部共用：

- **同一个 key。** On-chain DEX API 端点的 key 两个产品都能调，没有单独的 Wallet API 端点或 key。
- **同一份配额和速率限制。** Wallet API 的调用计入同一份 Crypto Data API 配额，与 RPC Requests 分开。
- **同一套价格。** Wallet API 没有单独的价目表，以 [Crypto Data API 定价](https://blockreq.com/zh/crypto-data-api/pricing)为准。

```
https://api.blockreq.com/v1/dex
```

支持的链：**Ethereum**（`ethereum`）、**Base**（`base`）和 **Arbitrum**（`arbitrum`）。Base 的持有人排行仍在回填（2026 年 11 月），所以目前 Base 上任何代币的持有人路由都返回 `404`。

## 认证

在 Dashboard 创建 **On-chain DEX API** 端点（**Endpoints → + Add Endpoint**），把它的 key 放在 `X-API-Key` 头里发送，也可以用 `Authorization: Bearer <key>`。key 的作用范围、RPC key 过渡期和匿名访问都与 On-chain DEX API 相同，见[获取 key](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md)。

```bash
export BLOCKREQ_DATA_API_KEY="你的 On-chain DEX API key"
```

本页的每个请求计 **1 次调用**。配额、速率限制、`429` 的处理和用量响应头在 [套餐与额度](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md) 和 [限制与 429](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md) 里统一说明，两个产品通用。

也可以在 [Wallet API 产品页](https://blockreq.com/zh/wallet-api)的 tester 里不带 key 试持有人和持仓路由。

## 路由

| 方法和路径                                         | 返回                                   | 状态     |
| -------------------------------------------------- | -------------------------------------- | -------- |
| `GET /v1/dex/{chain}/tokens/{address}/holders`     | 已跟踪代币的最大持有人                 | 已上线   |
| `GET /v1/dex/{chain}/accounts/{address}/portfolio` | 一个地址持有的代币及美元价值           | 已上线   |
| `GET /v1/dex/{chain}/pools/{id}/trades`            | 池子的成交，每笔带交易者地址           | 已上线   |
| `GET /v1/dex/{chain}/traders/{address}/positions`  | 交易者的仓位、平均成本、已实现与未实现盈亏 | 即将开放 |

数量都是字符串：`balance` 等原始数量是以最小单位计的整数（未按 decimals 换算）；`amount`、价格和美元金额是小数。响应里的地址都是小写。

## 代币持有人

```
GET /v1/dex/{chain}/tokens/{address}/holders
```

按代币的 ERC-20 Transfer 统计的前 `limit` 名持有人。销毁地址、Uniswap v4 PoolManager 和持有该代币的池子本身不参与排名，也不计入 `pct` 的分母。

只有已跟踪的代币有排行。锚定代币（稳定币和 WETH、USDC 这类主流币）、不在任何已索引池子里的代币、被屏蔽的代币，以及还没追上链头的代币都返回 `404`。新跟踪的代币要从链的创世块开始回填，完成之前也返回 `404`。

| 参数      | 位置  | 说明                                  |
| --------- | ----- | ------------------------------------- |
| `chain`   | path  | `ethereum`、`base` 或 `arbitrum`      |
| `address` | path  | 代币地址，`0x` 加 40 位十六进制，大小写不限 |
| `limit`   | query | 最多返回的持有人数，1–5000（默认 5000） |

**响应字段**（`data`）：

| 字段                 | 类型                   | 说明                                                                                                  |
| -------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `chain`、`address`   | string                 | 链和代币                                                                                              |
| `symbol`、`decimals` | string、integer / null | 代币元数据                                                                                            |
| `block`              | integer                | 统计到这个区块为止的 Transfer                                                                         |
| `known_total`        | 原始数量               | 除排除地址外所有余额之和，`pct` 的分母                                                                |
| `known_total_amount` | decimal / null         | `known_total` 换算成整币；decimals 未知时为 null                                                      |
| `total_supply`       | 原始数量 / null        | 从 BlockReq 自己的节点读到的 `totalSupply()`；读不到时为 null                                         |
| `consistent`         | boolean / null         | Transfer 合计与 `total_supply` 是否相差在 1% 以内。为 false 时（rebase 类代币等）所有 `pct` 都是 null |
| `excluded`           | string[]               | 被排除的销毁地址、PoolManager 和池子                                                                  |
| `top[]`              | object[]               | 余额从大到小：`rank`（最大为 1）、`address`、`balance`（原始）、`amount`（整币）、`pct`（6 位小数）   |

**示例：**Arbitrum 上 ARB 的前 20 名持有人。

```bash
curl -s "https://api.blockreq.com/v1/dex/arbitrum/tokens/0x912ce59144191c1204e64559fe8253a0e49e6548/holders?limit=20" \
  -H "X-API-Key: $BLOCKREQ_DATA_API_KEY"
```

```js
const res = await fetch(
  "https://api.blockreq.com/v1/dex/arbitrum/tokens/0x912ce59144191c1204e64559fe8253a0e49e6548/holders?limit=20",
  { headers: { "X-API-Key": process.env.BLOCKREQ_DATA_API_KEY } },
);
const { data } = await res.json();
for (const holder of data.top) console.log(holder.rank, holder.address, holder.pct);
```

```python
import os, requests

res = requests.get(
    "https://api.blockreq.com/v1/dex/arbitrum/tokens/0x912ce59144191c1204e64559fe8253a0e49e6548/holders",
    params={"limit": 20},
    headers={"X-API-Key": os.environ["BLOCKREQ_DATA_API_KEY"]},
    timeout=30,
)
for holder in res.json()["data"]["top"]:
    print(holder["rank"], holder["address"], holder["pct"])
```

`limit=2` 的响应（这里截短了 `excluded`；ARB 的 `excluded` 会列出持有它的每个池子）：

```json
{
  "data": {
    "chain": "arbitrum",
    "address": "0x912ce59144191c1204e64559fe8253a0e49e6548",
    "symbol": "ARB",
    "decimals": 18,
    "block": 522352929,
    "known_total": "9982381908048402768748531744",
    "known_total_amount": "9982381908.048402768748531744",
    "excluded": [
      "0x0000000000000000000000000000000000000000",
      "0x000000000000000000000000000000000000dead",
      "0xdead000000000000000042069420694206942069"
    ],
    "total_supply": "9999998977610261816650915825",
    "consistent": true,
    "top": [
      {
        "rank": 1,
        "address": "0xf3fc178157fb3c87548baa86f9d24ba38e649b58",
        "balance": "2556141991509656323975221470",
        "amount": "2556141991.50965632397522147",
        "pct": "25.606534"
      },
      {
        "rank": 2,
        "address": "0xf7f468b184a48f6ca37eeffe12733ee1c16b6e26",
        "balance": "449339761507879669910372383",
        "amount": "449339761.507879669910372383",
        "pct": "4.501328"
      }
    ]
  }
}
```

## 地址持仓

```
GET /v1/dex/{chain}/accounts/{address}/portfolio
```

一个地址在该链上持有的代币，只有两个来源：

1. 持有人排行里存的余额：该地址在其前 5000 名持有人之内的已跟踪代币（`source: "holders"`）；
2. 对你在 `tokens` 里列出的代币，从 BlockReq 自己的节点读 `balanceOf`（`source: "node"`），并替换这些代币存的余额；余额为 0 的代币不返回。

没有全量钱包索引：既不是该地址进了前 5000 名的已跟踪代币、也没列在 `tokens` 里的代币不会出现。按美元价值从大到小排序，没有美元价值的排在最后。什么都没找到的地址返回 `200` 和空的 `holdings` 列表。

| 参数      | 位置  | 说明                                                                                      |
| --------- | ----- | ----------------------------------------------------------------------------------------- |
| `chain`   | path  | `ethereum`、`base` 或 `arbitrum`                                                          |
| `address` | path  | 持有人地址，`0x` 加 40 位十六进制，大小写不限                                             |
| `tokens`  | query | 最多 50 个逗号分隔的 ERC-20 地址（重复的只算一次），用 `balanceOf` 读余额；原生币地址会被拒绝 |

**响应字段**（`data`）：

| 字段                        | 类型              | 说明                                                  |
| --------------------------- | ----------------- | ----------------------------------------------------- |
| `chain`、`address`          | string            | 链和持有人                                            |
| `total_value_usd`           | decimal           | 所有 `value_usd` 之和，6 位小数；没有美元价值的不计入 |
| `holdings[]`                | object[]          | 见下                                                  |
| `node_block`                | integer / null    | 节点 `balanceOf` 读取所在区块；没读时为 null          |
| `failed`                    | string[]          | `balanceOf` 失败的代币；这些代币保留存的余额          |
| `holdings[].token`          | string            | 代币                                                  |
| `holdings[].symbol`、`name` | string / null     | 代币元数据                                            |
| `holdings[].decimals`       | integer / null    |                                                       |
| `holdings[].balance`        | 原始数量          |                                                       |
| `holdings[].amount`         | decimal / null    | 换算成整币的余额；decimals 未知时为 null              |
| `holdings[].price_usd`      | decimal / null    |                                                       |
| `holdings[].value_usd`      | decimal / null    | `amount` × `price_usd`，6 位小数；缺一项时为 null     |
| `holdings[].source`         | `holders`、`node` | 排行里存的余额，或 BlockReq 节点上的 `balanceOf`      |
| `holdings[].block`          | integer           | 余额对应的区块                                        |

**示例：**Ethereum 上一个交易所钱包，另外实时读它的 USDC 余额。

```bash
curl -s "https://api.blockreq.com/v1/dex/ethereum/accounts/0x28c6c06298d514db089934071355e5743bf21d60/portfolio?tokens=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" \
  -H "X-API-Key: $BLOCKREQ_DATA_API_KEY"
```

```js
const url = new URL("https://api.blockreq.com/v1/dex/ethereum/accounts/0x28c6c06298d514db089934071355e5743bf21d60/portfolio");
url.searchParams.set("tokens", "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48");
const res = await fetch(url, { headers: { "X-API-Key": process.env.BLOCKREQ_DATA_API_KEY } });
const { data } = await res.json();
console.log(data.total_value_usd, data.holdings.length);
```

```python
import os, requests

res = requests.get(
    "https://api.blockreq.com/v1/dex/ethereum/accounts/0x28c6c06298d514db089934071355e5743bf21d60/portfolio",
    params={"tokens": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},
    headers={"X-API-Key": os.environ["BLOCKREQ_DATA_API_KEY"]},
    timeout=30,
)
data = res.json()["data"]
print(data["total_value_usd"], len(data["holdings"]))
```

不带 `tokens` 的响应（12 项持仓只列前 2 项）：

```json
{
  "data": {
    "chain": "ethereum",
    "address": "0x28c6c06298d514db089934071355e5743bf21d60",
    "total_value_usd": "133198.620099",
    "holdings": [
      {
        "token": "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da",
        "symbol": "GALA",
        "name": "Gala",
        "decimals": 8,
        "balance": "496270440907221534",
        "amount": "4962704409.07221534",
        "price_usd": "0.0000268169615577973807",
        "value_usd": "133084.653361",
        "source": "holders",
        "block": 26369822
      },
      {
        "token": "0x03ab458634910aad20ef5f1c8ee96f1d6ac54919",
        "symbol": "RAI",
        "name": "Rai Reflex Index",
        "decimals": 18,
        "balance": "33000000000000000000",
        "amount": "33",
        "price_usd": "3.01662215219692683",
        "value_usd": "99.548531",
        "source": "holders",
        "block": 26369822
      }
    ],
    "node_block": null,
    "failed": []
  }
}
```

在没有配置节点的链上带 `tokens`，或节点没有响应时，返回 `503 unavailable`，message 会说明是哪种情况。去掉 `tokens` 重试即可拿到存的余额。

## 池子成交

```
GET /v1/dex/{chain}/pools/{id}/trades
```

与 [On-chain DEX API](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md) 文档里的是同一条路由；用于钱包场景时，每笔成交都带 `trader`，即交易的发送者。按时间从新到旧。满页时带 `next_cursor`，作为 `cursor` 传入取下一页。原始成交保留期：已上架的池子 90 天，其他池子 30 天；更早的历史在 K 线里。

| 参数     | 位置  | 说明                                                              |
| -------- | ----- | ----------------------------------------------------------------- |
| `chain`  | path  | `ethereum`、`base` 或 `arbitrum`                                  |
| `id`     | path  | 池子：`0x` 加 40 位（池子合约）或 64 位（Uniswap v4 pool key）十六进制 |
| `limit`  | query | 每页最多成交数，1–100（默认 50）                                  |
| `cursor` | query | 上一页的 `next_cursor`；其他值返回 `400`                          |

**响应字段：**`data[]` 是成交列表，字段有 `block_number`、`log_index`、`time`（ISO 8601）、`tx_hash`、`trader`（地址或 null）、`amount0` 和 `amount1`（原始数量，从池子的角度：正数表示池子收到）、`price`（每个整币 token0 值多少整币 token1）、`amount_usd`、`price0_usd` 和 `price1_usd`（decimal 或 null）。最后一页的 `next_cursor` 为 null。响应还带 `X-Data-Tier` 头。

OpenAPI 文档里还定义了 `earliest_available`（保留窗口起点，Unix 秒）。截至 2026-11-08，生产 API 还没有返回这个字段，出现之前请不要依赖它。

```bash
curl -s "https://api.blockreq.com/v1/dex/ethereum/pools/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640/trades?limit=20" \
  -H "X-API-Key: $BLOCKREQ_DATA_API_KEY"
```

```js
let cursor = null;
do {
  const url = new URL("https://api.blockreq.com/v1/dex/ethereum/pools/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640/trades");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);
  const res = await fetch(url, { headers: { "X-API-Key": process.env.BLOCKREQ_DATA_API_KEY } });
  const page = await res.json();
  for (const trade of page.data) console.log(trade.time, trade.trader, trade.amount_usd);
  cursor = page.next_cursor;
} while (cursor);
```

```python
import os, requests

res = requests.get(
    "https://api.blockreq.com/v1/dex/ethereum/pools/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640/trades",
    params={"limit": 20},
    headers={"X-API-Key": os.environ["BLOCKREQ_DATA_API_KEY"]},
    timeout=30,
)
for trade in res.json()["data"]:
    print(trade["time"], trade["trader"], trade["amount_usd"])
```

`limit=1` 的响应：

```json
{
  "data": [
    {
      "block_number": 26369898,
      "log_index": 55,
      "time": "2026-11-08T11:37:11.000Z",
      "tx_hash": "0x4a2c1e87ccb499d442452a2ef17abafc4c36c7019768c9940fe748b46768d23a",
      "trader": "0x8dbee8f3917049bc30fef01924a7ac79d16cf2b9",
      "amount0": "6839954413",
      "amount1": "-3056128806869109546",
      "price": "0.000446805434998315031",
      "amount_usd": "6839.954413",
      "price0_usd": null,
      "price1_usd": null
    }
  ],
  "next_cursor": "1794137831000.26369898.55"
}
```

## 即将开放：仓位与盈亏

```
GET /v1/dex/{chain}/traders/{address}/positions
```

**尚未开放。**截至 2026-11-08，这个路径返回 `404`。这里提前写出来方便你规划；下面的字段是根据 OpenAPI 文档整理的草案，开放前仍可能调整。上线之前不提供示例数据。

开放后，这条路由会返回交易者在已上架池子里交易过的每个代币的仓位，按最近交易时间排序，用加权平均成本计算：买入把美元金额加进成本；卖出实现的盈亏是卖出的美元金额减去所卖部分的平均成本。通过转账进来的代币没有成本：卖出它们计入 `unmatched_sell_qty`，不产生已实现盈亏。未实现盈亏按代币当前美元价格给持有量估值。仓位每小时重算一次。

| 参数      | 位置  | 说明                               |
| --------- | ----- | ---------------------------------- |
| `address` | path  | 交易者（交易发送者），`0x` 开头    |
| `limit`   | query | 最多返回的仓位数，1–1000（默认 100） |

| 字段（草案）                  | 类型                                  | 说明                                                                                  |
| ----------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------- |
| `token`、`symbol`、`decimals` | string、string / null、integer / null | 代币                                                                                  |
| `qty`                         | 原始数量                              | 以最小单位计的持有量；卖出转账进来的代币后可能为负                                    |
| `unmatched_sell_qty`          | 原始数量                              | 超出已索引买入量的卖出数量，不产生已实现盈亏                                          |
| `cost_usd`                    | decimal                               | 持有量按加权平均成本计算的美元成本                                                    |
| `avg_cost_usd`                | decimal / null                        | 平均买入价：每个整币的美元成本；`qty` 不为正或 decimals 未知时为 null                 |
| `realized_pnl_usd`            | decimal                               | 已实现盈亏（美元）                                                                    |
| `price_usd`                   | decimal / null                        | 代币当前美元价格                                                                      |
| `unrealized_pnl_usd`          | decimal / null                        | 整币持有量 × `price_usd` − `cost_usd`；没有价格或 decimals 时为 null，`qty` 不为正时为 0 |
| `buys`、`sells`               | integer                               | 买入、卖出次数                                                                        |
| `first_day`、`last_day`       | date                                  | 第一笔和最近一笔已索引交易的 UTC 日期，`YYYY-MM-DD`                                   |

## 错误

错误是 JSON `{"error": {"code": "...", "message": "..."}}`。请按 `code` 分支处理，不要按 `message`。

| 状态 | `error.code`                            | 何时                                                                                          |
| ---- | --------------------------------------- | --------------------------------------------------------------------------------------------- |
| 400  | `invalid_request`                       | 参数不合法（地址格式错误、`limit` 超出范围、`cursor` 无效）                                   |
| 401  | `invalid_key`                           | key 不存在或已吊销，或没有放在 `X-API-Key` / `Authorization: Bearer` 里                       |
| 403  | `forbidden`                             | 账户被停用，或 key 的端点已过期、不允许你的 IP                                                |
| 404  | `not_found`                             | 不支持该链、代币还没有持有人排行，或池子未被索引                                              |
| 429  | `rate_limited`、`quota_exceeded`        | 超出速率，或本周期配额用完：见[限制与 429](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md)          |
| 500  | `internal`                              | 意外故障                                                                                      |
| 502  | `upstream_failed`、`response_too_large` | 数据服务失败或返回内容过大；退避后重试                                                        |
| 503  | `unavailable`                           | 短暂不可用，或无法从节点读取 `tokens`；稍后重试                                               |
| 504  | `timeout`                               | 超时；请重试                                                                                  |

## 另见

- [On-chain DEX API](https://blockreq.com/docs/zh-cn/build/onchain-dex-api/index.md)：价格、池子、K 线和 WebSocket 订阅，使用同一个 key
- [交互式参考（Swagger UI）](https://blockreq.com/docs/build/onchain-dex-api/reference/) 和 [OpenAPI JSON](https://blockreq.com/docs/build/onchain-dex-api/openapi.json)
- [Crypto Data API 定价](https://blockreq.com/zh/crypto-data-api/pricing)，On-chain DEX API 与 Wallet API 共用
