Wallet balances
Per-wallet token balances with optional USD valuation via SDP.
GET /v1/payments/wallets/{walletId}/balances returns a custody wallet's native SOL balance and every SPL token it holds, with raw and human-readable amounts, decimals, and a USD valuation where SDP has a price. SOL always comes first, as token: "SOL" with mint: "So11111111111111111111111111111111111111112". Token accounts with a zero balance are left out.
Request
walletId is the provider wallet ID: the walletId field from GET /v1/wallets, not the wallet's id (cwlt_…) or its address. The request needs an API key with wallets.read; a key limited to selected wallets reads only those.
curl 'https://api.solana.com/v1/payments/wallets/privy_wallet_123/balances' \
-H "Authorization: Bearer $SDP_API_KEY"const res = await fetch("https://api.solana.com/v1/payments/wallets/privy_wallet_123/balances", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.SDP_API_KEY}`
}
});HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.solana.com/v1/payments/wallets/privy_wallet_123/balances"))
.header("Authorization", "Bearer sf_...")
.GET()
.build();If SDP can't read the SOL balance or any token balance, the request returns 503 instead of partial or zero balances. To read state no older than a slot you already saw, pass minimumSlot; the response echoes it in data.balanceReadContext.minimumSlot.
Response shape
The payload sits in the standard data / meta envelope, with the balances in data.walletBalances.balances:
{
"data": {
"walletBalances": {
"walletId": "privy_wallet_123",
"address": "3xYZa...2aBc",
"balances": [
{
"token": "SOL",
"mint": "So11111111111111111111111111111111111111112",
"amount": "2500000000",
"uiAmount": "2.5",
"decimals": 9
},
{
"token": "USDC",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "1500000000",
"uiAmount": "1500",
"decimals": 6,
"usdPrice": 1,
"usdValue": 1500
}
]
}
},
"meta": { "requestId": "req_...", "timestamp": "2026-05-18T00:00:00.000Z" }
}Per-balance fields:
| Field | Notes |
|---|---|
token | Token symbol if known, otherwise the onchain mint. |
mint | Onchain mint address. |
amount | Raw amount in the smallest unit (a string, to preserve precision). |
uiAmount | Human-readable decimal string: amount / 10^decimals. |
decimals | The token's decimal count. |
usdPrice | Per-token USD price; omitted when no price is available. |
usdValue | Total USD value (usdPrice * uiAmount); omitted when no price is available. |
confidential | Reserved for confidential transfer balances; absent for standard balances. |
Use cases
- Account dashboards: show a custody wallet's contents to its operator.
- Pre-transfer balance checks: confirm a wallet holds enough of a token before you start a payout batch.
- Treasury accounting: take periodic snapshots into an internal ledger.
Pricing freshness
SDP prices balances only on mainnet. In a sandbox project on devnet, usdPrice and usdValue are always absent.
usdPrice is a display estimate from Tokens.xyz. SDP caches each price for up to 60 seconds. Some source snapshots refresh hourly; SDP omits snapshots older than two hours or without a timestamp. The snapshot timestamp doesn't prove when its underlying price was observed.
Use an executable quote for a trade and an independently verified valuation for accounting. The onchain amount and uiAmount remain the balance values. When pricing is unavailable, usdPrice and usdValue are absent: the omission means "unknown," not "$0.00."
Compared with onchain RPC
You can also list SPL token balances by calling getTokenAccountsByOwner on a Solana RPC node and decoding the token-account data. Use SDP's balances endpoint for:
- Token resolution: SDP resolves the mint to a symbol when it knows one; raw RPC returns mints only.
- USD pricing: RPC has no prices.
- One fewer dependency: no separate RPC integration when you already call SDP.
Go to RPC directly for:
- State at an exact slot: for an audit that needs the wallet as of one slot. SDP's
minimumSlotsets only a lower bound. - Confidential transfer balances: full extension support is RPC-side.
- Latency-sensitive paths: RPC is one hop closer to the cluster.
Related
- Payouts and disbursements: check balances before running a batch.
- Create a wallet: provision the wallets whose balances this endpoint reports.