钱包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
);
import requests
def get_balance_at(wallet: str, mint: str, time: int):
url = f"https://api.helius.xyz/v1/wallet/{wallet}/balance-at"
headers = {"X-Api-Key": "YOUR_API_KEY"}
params = {"mint": mint, "time": time}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
result = response.json()
if result["asOf"] is None:
print("Wallet had no activity for this token by that time — balance is 0")
return result
print(f"Balance: {result['balance']}")
print(f"Raw amount: {result['balanceRaw']} ({result['decimals']} decimals)")
print(f"As of slot {result['asOf']['slot']}, signature {result['asOf']['signature']}")
return result
# USDC balance on 2025-01-10 19:20:00 UTC
get_balance_at(
"5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
1736536800
)
curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&time=1736536800&api-key=YOUR_API_KEY"
查询特定日期时间的代币余额
传递可读日期时间而不是时间戳。记得将空格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"
查询参数
| 参数 | 必需 | 类型 | 描述 |
|---|
mint | 是 | string | 代币铸币地址。对于原生SOL,使用So11111111111111111111111111111111111111111。 |
time | 单选 | int | Unix时间戳(以秒为单位)。此时间的余额。 |
datetime | 单选 | string | 日期时间字符串,例如2025-01-10 19:20:00。默认UTC。 |
slot | 单选 | int | 插槽编号。此插槽的余额。精确且确定性。 |
time, datetime, 或 slot 中必须提供一个。提供零个或多个则返回400错误。
日期时间格式
接受的格式:
- 只有日期:
2025-01-10 → UTC午夜
- 日期+时间:
2025-01-10 19:20:00 或 2025-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: 读取余额的交易(slot,blockTime,signature)。
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 获取确定性结果。 time 和 datetime 通过验证器报告的区块时间解析,可能会漂移几秒。当精确的可重复性很重要时(快照、审计),通过 slot 查询。
- 将余额解析为字符串。
balance 和 balanceRaw 是字符串以保持精度。使用 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持有量及其美元价值。