跳转到主要内容
钱包API处于测试阶段。端点和响应格式可能会发生变化。

概览

历史余额端点回答:在过去某个特定时间点,此钱包的特定代币(或原生SOL)的余额是多少? 虽然余额端点报告当前持有情况,balance-at报告在任何时间戳、日期时间或槽位的持有情况。 它会找到在指定时间点或之前涉及钱包和代币的单个最近交易,然后读取该交易的交易后余额。交易的交易后余额是从该交易到下一个交易所持有的余额,因此“时间T的余额”是最后一个相关交易在T时间点(或槽位)或之前的区块时间的交易后余额。对于典型钱包而言,这是一个准确的值,而不是估计。
  • 代币(SPL / Token-2022):从交易的代币余额后读取,汇总钱包的代币账户中的此铸造代币。
  • 原生SOL:从交易的lamport后余额中读取。用伪铸币So11111111111111111111111111111111111111111表示原生SOL。

使用时机

使用历史余额API的情境:
  • 损益计算:确定某时期的起始和结束持有情况。
  • 成本基础和税务批次:重建收购或处置事件时的余额。
  • 争议解决:证明某钱包在特定时刻持有的内容。
  • 快照验证:检查钱包在空投或治理快照时的余额。
  • 会计和审计:在时期边界重建钱包状态。

快速开始

查询特定时间的代币余额

获取钱包在某个Unix时间戳的USDC余额:
const getBalanceAt = async (wallet, mint, time) => {
  const url = `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&time=${time}&api-key=YOUR_API_KEY`;

  const response = await fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const result = await response.json();

  if (result.asOf === null) {
    console.log('Wallet had no activity for this token by that time — balance is 0');
    return result;
  }

  console.log(`Balance: ${result.balance}`);
  console.log(`Raw amount: ${result.balanceRaw} (${result.decimals} decimals)`);
  console.log(`As of slot ${result.asOf.slot}, signature ${result.asOf.signature}`);

  return result;
};

// USDC balance on 2025-01-10 19:20:00 UTC
getBalanceAt(
  "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
  "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  1736536800
);

查询特定日期时间的代币余额

传递可读日期时间而不是时间戳。记得将空格URL编码为 %20
curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&datetime=2025-01-10%2019:20:00&api-key=YOUR_API_KEY"

特定插槽的原生SOL余额

对于原生SOL,使用伪造的So11111111111111111111111111111111111111111。基于插槽的查询是精确且确定性的:
curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=So11111111111111111111111111111111111111111&slot=313000000&api-key=YOUR_API_KEY"

查询参数

参数必需类型描述
mintstring代币铸币地址。对于原生SOL,使用So11111111111111111111111111111111111111111
time单选intUnix时间戳(以秒为单位)。此时间的余额。
datetime单选string日期时间字符串,例如2025-01-10 19:20:00。默认UTC。
slot单选int插槽编号。此插槽的余额。精确且确定性。
time, datetime, 或 slot 中必须提供一个。提供零个或多个则返回400错误。

日期时间格式

接受的格式:
  • 只有日期: 2025-01-10 → UTC午夜
  • 日期+时间: 2025-01-10 19:20:002025-01-10T19:20:00 (秒可选)→ UTC
  • 带明确时区:2025-01-10T19:20:00Z, 2025-01-10T19:20:00+02:00, 2025-01-10T19:20:00-05:00 → 按给定时区处理
无效或不支持的格式(01/10/2025, 2025-13-10, 2025-02-30)返回400错误。
日期时间默认解释为UTC。像2025-01-10 19:20:00这样的裸日期时间被视为UTC,而不是您的本地时间。如果您表示其他时区,请包括明确的时区偏移量。响应中的requested.time字段显示解析的纪元秒,以便您验证解释。

响应格式

{
  "wallet": "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "isNative": false,
  "balance": "284961463.392936",
  "balanceRaw": "284961463392936",
  "decimals": 6,
  "requested": {
    "time": 1736536800,
    "slot": null,
    "datetime": null
  },
  "asOf": {
    "slot": 313000000,
    "blockTime": 1736536794,
    "signature": "5Cyy7Mh9nVgFq3T8wJp2sKxR4dE6bA1uZoNcLrXmYqUpon"
  }
}

字段说明

  • wallet: 查询的钱包地址的回显。
  • mint: 查询的铸币(当为本地时为 SOL 伪铸币)的回显。
  • isNative: 当结果是本地 SOL 时为 true
  • balance: 以十进制字符串形式表示的人类可读金额——字符串而不是数字,因此大额余额不会失去精度。尾随的零被去除("1.5",而不是 "1.500000")。
  • balanceRaw: 以最小单位(SOL 的 lamports)表示的确切金额,作为字符串。
  • decimals: 代币小数位(SOL 为 9)。
  • requested: 查询的回显。当使用 datetime 时,time 也会填充为已解析的纪元秒数,使得 UTC 解释可以看到。
  • asOf: 读取余额的交易(slotblockTimesignature)。
asOf: null 表示为零,而不是错误。当钱包在请求时间点或之前没有匹配的交易时,端点返回 200,带有 balance: "0"asOf: null——钱包当时没有持有该代币。

用例

一段时间内的余额变化

比较两个时间点的持有量:
const getBalanceChange = async (wallet, mint, startTime, endTime) => {
  const fetchBalance = (time) =>
    fetch(
      `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&time=${time}&api-key=YOUR_API_KEY`
    ).then(r => r.json());

  const [start, end] = await Promise.all([
    fetchBalance(startTime),
    fetchBalance(endTime)
  ]);

  // balanceRaw is an exact integer string — use BigInt for precise arithmetic
  const delta = BigInt(end.balanceRaw) - BigInt(start.balanceRaw);
  const human = Number(delta) / 10 ** end.decimals;

  console.log(`Start: ${start.balance}`);
  console.log(`End: ${end.balance}`);
  console.log(`Change: ${human > 0 ? '+' : ''}${human}`);

  return { start, end, delta };
};

快照资格检查

验证钱包在快照槽中持有代币:
const heldAtSnapshot = async (wallet, mint, snapshotSlot, minimumRaw) => {
  const result = await fetch(
    `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&slot=${snapshotSlot}&api-key=YOUR_API_KEY`
  ).then(r => r.json());

  const eligible = BigInt(result.balanceRaw) >= BigInt(minimumRaw);
  console.log(`${wallet}: ${result.balance} at slot ${snapshotSlot}${eligible ? 'eligible' : 'not eligible'}`);

  return eligible;
};

最佳实践

  • 使用 slot 获取确定性结果。 timedatetime 通过验证器报告的区块时间解析,可能会漂移几秒。当精确的可重复性很重要时(快照、审计),通过 slot 查询。
  • 将余额解析为字符串。 balancebalanceRaw 是字符串以保持精度。使用 BigInt(balanceRaw)(或您的语言的任意精度整数)进行算术运算——不要强制转换为浮点数。
  • asOf: null 视为零。 null asOf 是一个成功的响应,表示钱包在请求点之前对该代币没有活动。不要将其处理为错误。
  • 缓存历史结果。 在过去某一点的余额从不改变。永久缓存结果以避免重复的 API 调用。

常见错误

错误代码描述解决方案
400缺少mint,无效的mint,或者time/datetime/slot的零或多个值,或无法解析的datetime提供一个有效的mint和仅一个时间点参数
401缺少或无效的API密钥确保在请求中包含您的API密钥
404路径中无效的钱包地址确认地址是有效的base58 Solana地址
429超出速率限制减少请求频率或升级您的计划
502上游RPC错误或超时使用指数回退重试

限制

  • 多代币账户钱包可能会少计。 余额从最近的匹配交易中读取。常见情况是每个mint一个关联代币账户是准确的。一个钱包持有多个代币账户的相同mint,其中最新交易只涉及其中一些账户,可能会少计。
  • 非常大余额的本机SOL精度。 对于超过约9,007,199 SOL (2⁵³ lamports)的SOL余额,精度可能在上游丢失。代币金额不受影响。
  • time/datetime精度取决于验证器报告的区块时间,可能会有几秒钟的漂移。使用slot获得精确、确定的结果。
  • 每次请求单个代币。 没有多mint或“在时间T的所有余额”批量形式。

下一步

钱包余额

获取钱包当前的代币和NFT持有量及其美元价值。

钱包API概览

所有钱包API端点和共享约定。

API参考

历史余额的请求和响应模式。