Parameters
Public key of the owner (base-58 encoded)
Configuration object containing:Public key of the token program (base-58 encoded)
Commitment level (processed, confirmed, finalized)
Encoding for account data (base58, base64, jsonParsed)
Response
Object containing:The slot the request was processed at
Array of token account objects containing:Public key of the token account (base-58 encoded)
Number of lamports in the account
Public key of the account owner (base-58 encoded)
Whether the account is executable
Epoch at which the account will next owe rent
Raw amount of tokens as a string
Number of decimals configured for token’s mint
Token amount as a float, accounting for decimals
Token amount as a string, accounting for decimals
Public key of the token’s mint (base-58 encoded)
Public key of the token account owner (base-58 encoded)
Public key of the delegate (base-58 encoded)
Type of account (account)
Program that owns the account (spl-token)
Number of bytes allocated to the account
Code Examples
Basic Request
curl https://rpc.orbitflare.com -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenAccountsByOwner",
"params": [
"4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj",
{
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
}
]
}'
Using web3.js
import { Connection, PublicKey } from '@solana/web3.js';
const connection = new Connection('https://rpc.orbitflare.com');
// Get token accounts by owner
const owner = new PublicKey('4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLZj');
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const accounts = await connection.getTokenAccountsByOwner(owner, { programId });
console.log('Token accounts:', accounts);
// Get token accounts by owner with analysis
async function getTokenAccountsByOwnerWithAnalysis(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
) {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
return {
accounts,
analysis: {
totalAccounts: accounts.value.length,
totalBalance: accounts.value.reduce((sum, acc) => {
const amount = BigInt(acc.account.data.parsed.info.tokenAmount.amount);
return sum + amount;
}, BigInt(0)),
byMint: accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
acc[mint] = (acc[mint] || BigInt(0)) + amount;
return acc;
}, {} as Record<string, bigint>),
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
}
};
}
Notes
- Returns all token accounts owned by the given public key
- The owner has full control over these accounts
- The response is immediate as it reads from the current state
- The accounts can change with token transfers and other operations
- The owner must be a valid public key
Best Practices
- Use appropriate commitment level based on your needs
- Cache results when appropriate to reduce RPC load
- Monitor for changes in owned accounts
- Consider using websocket subscription for real-time updates
- Handle network errors and retry when appropriate
Common Errors
| Code | Message | Solution |
| -32601 | Method not found | Verify you’re connected to a Solana RPC node |
| -32602 | Invalid params | Check owner public key and configuration |
| -32007 | Owner not found | Verify the owner exists |
| -32008 | Invalid program ID | Verify the program ID is correct |
Use Cases
-
Owner Account Analysis
interface OwnerAccountAnalysis {
owner: string;
accounts: Array<{
pubkey: string;
mint: string;
balance: string;
delegate?: string;
}>;
metrics: {
totalAccounts: number;
totalBalance: string;
byMint: Record<string, string>;
delegatedAccounts: number;
};
metadata: {
timestamp: number;
commitment?: string;
};
}
class OwnerAccountAnalyzer {
async analyzeOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise<OwnerAccountAnalysis> {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const byMint: Record<string, bigint> = {};
let totalBalance = BigInt(0);
let delegatedAccounts = 0;
accounts.value.forEach(account => {
const mint = account.account.data.parsed.info.mint;
const amount = BigInt(account.account.data.parsed.info.tokenAmount.amount);
const delegate = account.account.data.parsed.info.delegate;
byMint[mint] = (byMint[mint] || BigInt(0)) + amount;
totalBalance += amount;
if (delegate) {
delegatedAccounts++;
}
});
return {
owner: owner.toBase58(),
accounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
})),
metrics: {
totalAccounts: accounts.value.length,
totalBalance: totalBalance.toString(),
byMint: Object.fromEntries(
Object.entries(byMint).map(([mint, amount]) => [mint, amount.toString()])
),
delegatedAccounts
},
metadata: {
timestamp: Date.now(),
commitment: config.commitment
}
};
}
}
-
Owner Account Monitoring
interface OwnerAccountChange {
owner: string;
changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
previousDelegate?: string;
currentDelegate?: string;
}>;
metadata: {
timestamp: number;
};
}
class OwnerAccountMonitor {
private previousAccounts: Map<string, {
balance: string;
delegate?: string;
}> = new Map();
async monitorOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise<OwnerAccountChange | null> {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const currentAccounts = new Map(
accounts.value.map(account => [
account.pubkey,
{
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
}
])
);
const changes: Array<{
type: 'added' | 'removed' | 'modified';
account: string;
previousBalance?: string;
currentBalance?: string;
previousDelegate?: string;
currentDelegate?: string;
}> = [];
// Check for removed or modified accounts
this.previousAccounts.forEach((data, account) => {
if (!currentAccounts.has(account)) {
changes.push({
type: 'removed',
account,
previousBalance: data.balance,
previousDelegate: data.delegate
});
} else {
const current = currentAccounts.get(account)!;
if (current.balance !== data.balance || current.delegate !== data.delegate) {
changes.push({
type: 'modified',
account,
previousBalance: data.balance,
currentBalance: current.balance,
previousDelegate: data.delegate,
currentDelegate: current.delegate
});
}
}
});
// Check for added accounts
currentAccounts.forEach((data, account) => {
if (!this.previousAccounts.has(account)) {
changes.push({
type: 'added',
account,
currentBalance: data.balance,
currentDelegate: data.delegate
});
}
});
this.previousAccounts = currentAccounts;
if (changes.length > 0) {
return {
owner: owner.toBase58(),
changes,
metadata: {
timestamp: Date.now()
}
};
}
return null;
}
}
-
Owner Account Planning
interface OwnerAccountPlan {
owner: string;
currentAccounts: Array<{
pubkey: string;
mint: string;
balance: string;
delegate?: string;
}>;
recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance' | 'delegate';
accounts: string[];
reason: string;
}>;
metadata: {
timestamp: number;
};
}
class OwnerAccountPlanner {
private readonly minBalancePerAccount = BigInt(1000000); // 1 token
private readonly maxAccountsPerMint = 5;
async planOwnerAccounts(
owner: PublicKey,
config: { programId: PublicKey; commitment?: string }
): Promise<OwnerAccountPlan> {
const accounts = await connection.getTokenAccountsByOwner(owner, config);
const byMint = accounts.value.reduce((acc, account) => {
const mint = account.account.data.parsed.info.mint;
if (!acc[mint]) {
acc[mint] = [];
}
acc[mint].push(account);
return acc;
}, {} as Record<string, typeof accounts.value>);
const recommendations: Array<{
type: 'consolidate' | 'diversify' | 'rebalance' | 'delegate';
accounts: string[];
reason: string;
}> = [];
Object.entries(byMint).forEach(([mint, mintAccounts]) => {
// Check for accounts with low balances
const lowBalanceAccounts = mintAccounts.filter(account =>
BigInt(account.account.data.parsed.info.tokenAmount.amount) < this.minBalancePerAccount
);
if (lowBalanceAccounts.length > 0) {
recommendations.push({
type: 'consolidate',
accounts: lowBalanceAccounts.map(account => account.pubkey),
reason: `Accounts with balances below minimum threshold for mint ${mint}`
});
}
// Check for too many accounts per mint
if (mintAccounts.length > this.maxAccountsPerMint) {
recommendations.push({
type: 'consolidate',
accounts: mintAccounts.map(account => account.pubkey),
reason: `Too many accounts (${mintAccounts.length}) for mint ${mint}`
});
}
// Check for accounts without delegates
const accountsWithoutDelegates = mintAccounts.filter(
account => !account.account.data.parsed.info.delegate
);
if (accountsWithoutDelegates.length > 0) {
recommendations.push({
type: 'delegate',
accounts: accountsWithoutDelegates.map(account => account.pubkey),
reason: `Accounts without delegates for mint ${mint}`
});
}
});
return {
owner: owner.toBase58(),
currentAccounts: accounts.value.map(account => ({
pubkey: account.pubkey,
mint: account.account.data.parsed.info.mint,
balance: account.account.data.parsed.info.tokenAmount.amount,
delegate: account.account.data.parsed.info.delegate
})),
recommendations,
metadata: {
timestamp: Date.now()
}
};
}
}