getTokenAccountsByOwner
https://api.triport.ioReturns all SPL Token accounts owned by a given wallet, filtered by a single mint or token program.
getTokenAccountsByOwner returns every SPL Token account held by a wallet
(owner), narrowed by a required filter. You filter by exactly one of
mint (return only the owner's account(s) for that specific token) or
programId (return all of the owner's accounts owned by that token program,
e.g. the SPL Token program). You must supply one and only one of the two — not
both, and not neither.
Use it to discover a wallet's token holdings: which mints it holds, the address
of each token account, and — with jsonParsed encoding — the decoded balance
for each. Each entry in the result pairs the token account's pubkey with its
full on-chain account state, all read at a single slot so the snapshot is
internally consistent.
This is the standard Solana JSON-RPC method and works on any wallet. It is
distinct from the Digital Asset Standard
getTokenAccounts, which is a sol_das method
(basic+ tier) with cursor pagination and a different result shape;
getTokenAccountsByOwner here is sol_read_rpc and available from the free
tier.
Parameters
Positional params array: [owner, filter, config?].
ownerstring (base-58 Pubkey)required^[1-9A-HJ-NP-Za-km-z]{32,44}$.filterobjectrequiredmint or programId (see below).configobjectoptionalfilter (TokenAccountsFilter)objectmintstring (base-58 Pubkey)programIdstring (base-58 Pubkey)TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA for SPL Token).config (GetTokenAccountsConfig)objectencodingstringoptionalbase58, base64, base64+zstd, jsonParsed. jsonParsed decodes token balances into a readable object; base58 is limited to account data under 129 bytes.commitmentstringoptionalprocessed, confirmed, or finalized.dataSliceobjectoptional{ "offset": <integer>, "length": <integer> }. Only valid with the binary encodings.minContextSlotintegeroptionalResponse
With a binary encoding (e.g. base64), each account.data is instead a
[<data>, <encoding>] tuple rather than the decoded parsed object shown
above.
context.slotintegercontext.apiVersionstringvaluearrayvalue[i].pubkeystring (base-58)value[i].accountobjectAccountInfo for the token account.value[i].account.lamportsintegervalue[i].account.ownerstring (base-58)value[i].account.executablebooleanfalse for token accounts).value[i].account.rentEpochintegeru64).value[i].account.spaceintegervalue[i].account.dataobject | arrayjsonParsed: a decoded object with program/parsed. For binary encodings: a [<data>, <encoding>] tuple.Errors
Errors use the standard JSON-RPC envelope (error.code, error.message,
error.data).
| Code | HTTP | Meaning | When it happens |
|---|---|---|---|
-32602 | 400 | Invalid params | owner is not valid base-58, filter is missing or specifies both/neither of mint/programId, or config contains an invalid value (e.g. base58 encoding on data larger than 129 bytes). |
-32002 | 403 | Tier insufficient | API key's tier/scope does not include sol_read_rpc. |
-32003 | 429 | Rate limit exceeded | More than your tier's sustained RPS (with burst) was sent. Honour the Retry-After header. |
-32601 | 404 | Method not found | Method name misspelled or not available on this chain. |
Example rate-limit envelope:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32003,
"message": "Rate limit exceeded: 20 RPS sustained on sol_read_rpc (free tier)",
"data": {
"current_tier": "free",
"category": "sol_read_rpc",
"limit_rps": 20,
"burst_capacity": 40,
"retry_after_sec": 1
}
}
}See the shared errors reference for the full envelope and retry guidance.
Examples
JavaScript (fetch)
const res = await fetch("https://api.triport.io", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TRIPORT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "getTokenAccountsByOwner",
params: [
"4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA",
{ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" },
{ encoding: "jsonParsed", commitment: "finalized" },
],
}),
});
const { result } = await res.json();
result.value.forEach(({ pubkey, account }) => {
const { tokenAmount } = account.data.parsed.info;
console.log(pubkey, tokenAmount.uiAmountString);
});TypeScript SDK (@triport/sdk)
import { Triport } from "@triport/sdk";
const client = new Triport({ apiKey: process.env.TRIPORT_API_KEY! });
const { context, value } = await client.sol.getTokenAccountsByOwner(
"4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA",
{ mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" },
{ encoding: "jsonParsed", commitment: "finalized" },
);
console.log(`found ${value.length} accounts at slot ${context.slot}`);
for (const { pubkey, account } of value) {
console.log(pubkey, account.data.parsed.info.tokenAmount.uiAmountString);
}Python (triport-sdk)
import os
from triport import Triport
client = Triport(api_key=os.environ["TRIPORT_API_KEY"])
resp = client.sol.get_token_accounts_by_owner(
"4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA",
filter={"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},
encoding="jsonParsed",
commitment="finalized",
)
for entry in resp.value:
info = entry.account.data["parsed"]["info"]
print(entry.pubkey, info["tokenAmount"]["uiAmountString"])Notes
- Exactly one filter: the
filterobject must containmintorprogramId, never both and never neither. Usemintto look up a wallet's account for one specific token; useprogramId(e.g.TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) to enumerate all of the wallet's token accounts under that program. - Token-2022: to find Token-2022 accounts, filter by that program's
programIdrather than the legacy SPL Token program — the two live under different owning programs. - Encoding:
jsonParsedis the most convenient choice — it decodes mint, owner, andtokenAmountfor each account. Usebase64/base64+zstdwhen you need the raw account bytes;base58only works for data under 129 bytes, anddataSlice(binary encodings only) is ignored byjsonParsed. - Commitment: use
confirmedorprocessedfor lower latency when you can tolerate possible rollbacks;finalizedis the safest choice for settled state. - Empty result: a wallet with no matching token accounts returns an empty
valuearray (with a populatedcontext), not an error. - Related methods:
getTokenAccountsByDelegate(same shape, keyed by approved delegate instead of owner),getTokenAccounts(DAS variant,sol_das/ basic+ tier, cursor-paginated),getMultipleAccounts, andgetProgramAccounts(note its much strictersol_read_rpc_heavyrate limits of 2 / 5 / 20 / 80 rps).