Skip to content
NEW Earn 30% affiliate commissions Get your link

Solana token holder snapshot: how to export holders to CSV

A Solana holder snapshot reads every token account of a mint and sums balances per owner. For ORCA that meant 301,639 accounts but only 92,316 real holders.

12 min read
Cover image for Solana token holder snapshot: how to export holders to CSV

Key takeaways

  • A Solana token holder snapshot is a list of every owner wallet holding a mint at one slot, built by reading all token accounts of that mint and summing balances per owner.
  • In a November 4, 2026 read, the ORCA mint had 301,639 token accounts, but 209,019 of them (69%) were empty, leaving 92,316 owner wallets with a balance.
  • SOLTidy's free Holder Snapshot calls getProgramAccounts with a memcmp filter on the mint at offset 0, adds a 165-byte dataSize filter for SPL Token, and requests only 40 bytes per account.
  • Classic SPL token accounts are exactly 165 bytes, but Token-2022 accounts are larger when extensions are present, so a Token-2022 snapshot must filter on the mint only, without dataSize.
  • Before an airdrop, remove liquidity pool authorities, exchange hot wallets, the incinerator address and vesting vaults from the snapshot, or a large share of the drop goes to programs, not people.

The short answer

A Solana token holder snapshot is a list of every wallet that holds a given mint, with each wallet's balance, read at one slot. Solana keeps no holder list, so you build one by reading every token account of the mint with getProgramAccounts, dropping empty accounts and summing balances per owner. The difference matters: on November 4, 2026, the ORCA mint had 301,639 token accounts, but only 92,316 owner wallets held any ORCA. The free Holder Snapshot does this in a browser and exports CSV; Dune and indexer APIs cover mints too large for that.

This post is about the snapshot itself: methods, the exact query, and the cleanup. Planning the airdrop around it (eligibility rules, amounts, rent budget) is covered in how to airdrop tokens on Solana.

What is a Solana token holder snapshot?

A token holder snapshot is a point-in-time record of who owns a token, aggregated by owner wallet. It exists because of how Solana stores balances. A wallet does not hold tokens directly; for each mint it holds a token account, a separate 165-byte account whose first 32 bytes are the mint, the next 32 bytes the owner, and the next 8 bytes the raw amount as a little-endian u64 (SPL Token account layout).

Three facts follow from that layout:

  • Owner is not token account. The token account address is not the holder. The owner field is. Airdrops, allowlists and Discord checks almost always want the owner.
  • One owner can have several accounts. The associated token account is standard, but older wallets and programs created auxiliary accounts. Summing per owner prevents paying one wallet twice.
  • Most accounts may be empty. Selling a token leaves its account open with a zero balance until someone closes it. Counting accounts badly overstates holders.

The snapshot I took of ORCA shows how much each of these matters:

ORCA mint, read November 4, 2026Count
Token accounts (SPL Token, 165 bytes)301,639
Accounts with a zero balance209,019 (69%)
Owner wallets with a non-zero balance92,316
Owners holding the token in more than one account42
Raw response size with a 40-byte data slice85.6 MB
Time to return through SOLTidy's RPCabout 56 seconds

Dropping empty accounts is the big correction here; aggregation by owner changed the count by only 42 wallets. On other mints, especially ones a program distributed into non-associated accounts, aggregation matters more, so do both.

What are the ways to take a holder snapshot?

There are five practical routes, and the right one depends mostly on how many token accounts the mint has and whether you need history. As of November 2026:

MethodCostPractical max sizeAggregates by ownerCSVSpeed
SOLTidy Holder SnapshotFree, no walletA few hundred thousand token accountsYes, drops zero balancesCSV, JSON, TXTSeconds to about a minute
Raw getProgramAccounts (curl, script)Free to a few credits on your own RPCSame RPC limits; depends on providerYou write itYou write itSimilar
Dune solana_utils.latest_balancesFree to view; CSV export is a paid-plan featureAny mint, including USDCYes, with GROUP BYOn paid plans or via APIQuery runtime, index delay
Indexer API (Helius getTokenAccounts)Free tier has 1M credits; 10 credits per callAny, 1,000 accounts per pageYou write itYou write itMinutes for large mints
Explorer (Solscan Holders tab, Pro API)Web view free; Pro API from $49/monthTop holders in UI; API pages of 40Per token accountLimited in UISlow for full lists

getTokenLargestAccounts, a standard RPC method, deserves a mention because people try it first: it returns only the 20 largest token accounts, not owners. It is useful for spotting pools and whales, not for a snapshot.

How does SOLTidy's Holder Snapshot work?

I built the Holder Snapshot to do the standard RPC route correctly in a browser, with no wallet connection and no fee. Verified from its code, it works like this:

  1. Reads the mint. It fetches the mint account, checks whether it is owned by the SPL Token program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) or Token-2022 (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb), and reads decimals and supply. A token account or non-token address is rejected with a clear message.
  2. Queries every token account. It calls getProgramAccounts on that program with a memcmp filter matching the mint at offset 0, plus a dataSize: 165 filter for SPL Token only. It uses confirmed commitment and a dataSlice of offset 32, length 40, so each account returns only the owner and the amount instead of 165 bytes.
  3. Aggregates. Zero balances are dropped, amounts are summed per owner as BigInt raw integers, and the number of token accounts per owner is kept.
  4. Filters. You can set a minimum balance, keep the top N, and paste addresses to exclude.
  5. Exports. The CSV has the columns rank, owner, amount, percent_of_supply, token_accounts. The JSON adds the mint, program, decimals, raw supply and an ISO timestamp, with raw amounts as strings. The TXT is one address per line.

The panel also shows what share of supply the found balances add up to, which is a quick completeness check, and the top-10 concentration after your exclusions.

Its limit is size. The request has a 90-second timeout, and when the RPC refuses or times out, the tool stops with the error "This mint has too many token accounts for a browser snapshot." The code also contains a fallback that pages an indexed token-account API, but that only runs on a Helius endpoint. Since late October 2026 the site's RPC goes through a server proxy backed by Alchemy and public endpoints, so that fallback is currently inactive and very large mints simply fail with that message. ORCA-sized mints work; USDC-sized ones do not.

From the result, "Send to these holders" passes up to 20,000 addresses to the Solana Multisender, optionally with a total split pro-rata by balance. Above that, export the CSV and upload it there.

How do you take a snapshot with getProgramAccounts yourself?

There is no spl-token or solana CLI command that lists the holders of a mint; spl-token accounts lists only your own accounts. The command-line route is a JSON-RPC call with curl. This request returns every SPL Token account of a mint, parsed, with the slot it was read at:

RPC="https://your-rpc-provider-url"
MINT="orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE"

curl -s "$RPC" -H 'content-type: application/json' -d '{
  "jsonrpc": "2.0", "id": 1,
  "method": "getProgramAccounts",
  "params": [
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
    {
      "encoding": "jsonParsed",
      "withContext": true,
      "commitment": "confirmed",
      "filters": [
        { "dataSize": 165 },
        { "memcmp": { "offset": 0, "bytes": "'"$MINT"'" } }
      ]
    }
  ]
}' -o accounts.json

Then aggregate by owner and write CSV with jq:

jq -r '.result.context.slot as $slot
  | .result.value
  | map(.account.data.parsed.info
      | {owner, raw: (.tokenAmount.amount | tonumber), ui: (.tokenAmount.uiAmountString | tonumber)})
  | map(select(.raw > 0))
  | group_by(.owner)
  | map({owner: .[0].owner, amount: (map(.ui) | add), accounts: length})
  | sort_by(-.amount)
  | (["owner","amount","token_accounts","slot"] | @csv),
    (.[] | [.owner, .amount, .accounts, $slot] | @csv)' accounts.json > holders.csv

A few notes from testing this:

  • Token-2022. Replace the program ID with TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb and remove the dataSize filter. The Token-2022 accounts I read were 187 bytes, not 165, because of extensions; a 165 filter would silently return nothing.
  • Use your own RPC. The public api.mainnet-beta.solana.com endpoint rate-limits heavy calls; I got HTTP 429 on a simple getTokenLargestAccounts. A free key from Helius, Alchemy, QuickNode or Triton is the realistic starting point.
  • jsonParsed is heavy. It is convenient for small mints, but it inflates the response. For anything over tens of thousands of accounts, request base64 with "dataSlice": { "offset": 32, "length": 40 } as SOLTidy does, and decode the owner (32 bytes) and amount (u64 little-endian) in a script.
  • Precision. jq uses floating-point numbers, so very large raw amounts lose precision. Keep the raw amount string and use a language with big integers when exact payouts matter.
  • Record the slot. withContext: true returns context.slot. That number is your snapshot time; publish it with the list.

The parameters are documented in the getProgramAccounts reference.

How do you get token holders from Dune?

Dune's curated solana_utils.latest_balances table holds the most recent balance of every Solana address, including SPL token balances, and handles mints of any size. Its columns include address (the token account), token_mint_address, token_balance, token_balance_owner and block_slot (Dune docs). A holder query looks like this:

SELECT
  token_balance_owner AS owner,
  SUM(token_balance) AS amount,
  COUNT(*) AS token_accounts
FROM solana_utils.latest_balances
WHERE token_mint_address = 'orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE'
  AND token_balance > 0
GROUP BY 1
ORDER BY 2 DESC

Dune is the best option for USDC-sized mints and the only one here with history: solana_utils.daily_balances gives balances by day, so you can reconstruct a snapshot for a past date, and tokens_solana.transfers lets you trace how a balance got there. The tradeoffs are that the table lags the chain, and as of November 2026 Dune's documentation lists CSV export under paid plans and trials (one help page also mentions metered free exports, so check your account). Check updated_at before treating the result as a live snapshot.

Can you export token holders from Solscan?

Partly. Solscan's Holders tab on a token page lists holders ranked by balance and is fine for eyeballing the top of the list and finding pools. Its CSV reports are built for transfer history and are capped at the most recent 1,000 records, so they are not a route to a complete holder list for a large token. For full lists Solscan offers the Pro API endpoint /v2.0/token/holders, which pages 10 to 40 holders per request; the Lite plan is listed at $49 a month and Level 2 at $199 (Solscan API plans). For a one-off snapshot, the RPC or Dune routes are cheaper.

When should you use an indexer API?

Use an indexer when the mint is too big for a single getProgramAccounts response and you want live data rather than Dune's lag. Helius's DAS method getTokenAccounts takes a mint, returns token accounts with their owner and amount 1,000 at a time with a cursor, and reports a last_indexed_slot (Helius docs). On Helius's free plan, with 1 million credits a month and 10 credits per DAS call, a million-account mint costs about 10,000 credits and a few minutes of paging. You still aggregate per owner yourself.

How do you snapshot NFT holders?

An NFT collection needs a different query, because every NFT is its own mint. The collection's member list comes from a DAS index: getAssetsByGroup with groupKey: "collection" and the verified collection mint, or getAssetsByCreator with the first verified creator for collections minted before Metaplex certified collections. Both return up to 1,000 assets per page with each asset's current owner, including compressed NFTs.

SOLTidy's NFT Snapshot & Hashlist is built on exactly those calls: it pages up to a 50,000-NFT cap, drops burnt assets, counts NFTs per owner and exports a holders CSV (owner, count), the hashlist as JSON or TXT, and an owners list. It depends on the RPC answering DAS methods. As of November 4, 2026, SOLTidy's current RPC provider returns "Method not found" for getAssetsByGroup, so the tool shows its "needs a DAS-capable RPC endpoint" message until a DAS provider is back. Until then, the same call with a free Helius key works:

curl -s "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY" -H 'content-type: application/json' -d '{
  "jsonrpc": "2.0", "id": 1, "method": "getAssetsByGroup",
  "params": { "groupKey": "collection", "groupValue": "COLLECTION_MINT", "page": 1, "limit": 1000 }
}'

Two NFT-specific pitfalls:

  • Collection vs creator. The collection mint, shown as the verified Collection field on any NFT in Solscan or SolanaFM, is the reliable key. A creator address can cover several collections at once, and marketplaces sometimes add their own verified creator.
  • Escrows and staking vaults. A listed NFT on an escrow marketplace, or a staked NFT, shows the escrow or vault as owner. Exclude those addresses or the reward goes to a program.

What should you remove from a holder snapshot?

Remove every address that is not a person who should qualify, before you set amounts. The common ones:

Address typeHow to spot itWhy remove it
Liquidity pool authorityLargest holders; owner is a Raydium, Orca or Meteora PDA (Raydium AMM v4 uses 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1)Tokens sent to a pool vault are unrecoverable or end up as pool liquidity
Exchange hot walletsLabeled on Solscan as Binance, Coinbase, OKX and so onOne address represents thousands of customers
Burn address1nc1nerator11111111111111111111111111111111Nobody can spend from it
Vesting, lockers, treasury, teamProgram-owned vaults, multisigs, your own walletsUsually excluded by the eligibility rule
Bots and sybilsMany wallets with near-identical balances funded from one sourceSplitting one holding across wallets games equal-amount drops

Paste these into the exclude box in the Holder Snapshot and the CSV, top-10 figure and handoff all update. To check what an address actually is, the Batch Balance Checker takes up to 1,000 addresses and labels each one as a wallet, an executable program, or a PDA/program-owned account, with the owning program shown. Program-owned addresses in a recipient list are almost always mistakes. For more on pool and burn mechanics, see burned vs locked liquidity.

Sybil filtering has no clean on-chain answer. A minimum balance and pro-rata amounts blunt it, since splitting a holding no longer earns more; tracing funding sources catches clusters but takes manual work.

What are the other common snapshot mistakes?

The rest of the mistakes are about units, programs and timing:

  • Decimals. Raw amounts are integers. A token with 6 decimals stores 1 token as 1,000,000. SOLTidy's CSV shows whole tokens; raw RPC output does not. Always read decimals from the mint.
  • Wrong program. Querying the SPL Token program for a Token-2022 mint returns zero accounts without an error. Check the mint's owner first; Token-2022 explained covers how to tell them apart.
  • Timing. getProgramAccounts returns the state at the slot the node is on, not a past moment. Announce the snapshot time in advance, take it then, and save the file with the slot or timestamp. If you need yesterday, use Dune's daily_balances.
  • Snapshot gaming. Wallets that buy just before an announced snapshot and sell after it are hard to stop with a single snapshot. Some projects take several unannounced snapshots in a window and use each wallet's minimum balance.

How do you take a snapshot and export it to CSV, step by step?

For a mint up to a few hundred thousand token accounts:

  1. Copy the token's mint address from Solscan, SolanaFM or Jupiter. Not a token account and not the pool address.
  2. Open the Holder Snapshot, paste the mint and press Take snapshot. No wallet is needed.
  3. Check the share-of-supply figure. Near 100% means no account was missed; a clearly lower figure means some accounts were not returned, so retry before you rely on the list.
  4. Paste pool authorities, exchange wallets, the burn address and team wallets into the exclude box. Set a minimum balance if your rule has one.
  5. Export CSV and keep it with the timestamp as your published record.
  6. Send the list to the multisender, with a pro-rata total if the drop is proportional. It charges 0.0009 SOL per recipient plus rent for new token accounts, as the airdrop guide explains.

If the tool reports that the mint has too many token accounts, move to Dune or an indexer API as above.

Bottom line

A Solana holder snapshot is every token account of a mint at one slot, with empty accounts dropped and balances summed per owner; for ORCA that turned 301,639 accounts into 92,316 holders. For mints up to a few hundred thousand accounts, the free Holder Snapshot does it in about a minute and exports CSV, and a getProgramAccounts call with a mint memcmp filter is the do-it-yourself version. For USDC-sized mints or past dates, use Dune or a paging indexer. Whatever the route, query the right token program, respect decimals, record the slot, and strip pools, exchanges and burn addresses before the list reaches the multisender.

Questions & answers

How do I get a list of all holders of a Solana token?

Read every token account of the mint and group them by owner. You can do it without code in SOLTidy's free Holder Snapshot: paste the mint, take the snapshot and export CSV. With code, call getProgramAccounts on the token program with a memcmp filter on the mint at offset 0, drop zero balances and sum per owner. Dune's solana_utils.latest_balances table and indexer APIs such as Helius getTokenAccounts are the alternatives for very large mints.

Why is the holder count on Solscan different from my snapshot?

Explorers often count token accounts rather than owner wallets, may include empty accounts, and refresh their index on a delay. A snapshot that drops zero balances and sums per owner usually shows fewer holders. For ORCA on November 4, 2026, 301,639 token accounts reduced to 92,316 owners with a balance, because 69% of the accounts were empty.

Can I take a snapshot of Solana token holders at a past date?

Not with getProgramAccounts, which only returns the current state at the slot the node is on. For a past date you need historical data: Dune's solana_utils.daily_balances table, an indexer that stores history, or a snapshot you took and saved at the time. That is why projects announce the snapshot time in advance and take it live.

Does a token holder snapshot work for Token-2022 tokens?

Yes, but you must query the Token-2022 program (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb) instead of the SPL Token program, and drop the dataSize 165 filter, because Token-2022 accounts with extensions are larger. SOLTidy's Holder Snapshot checks which program owns the mint and adjusts the query automatically.

How do I take a snapshot of an NFT collection's holders?

NFTs are separate mints, so a single-mint token snapshot does not help. You need the collection's member list, which comes from a DAS index: getAssetsByGroup with the verified collection mint, or getAssetsByCreator with the first verified creator for older collections. Each page returns up to 1,000 NFTs with their current owners, which you then count per wallet.

Why does the snapshot fail for USDC or other huge tokens?

Mints with millions of token accounts produce responses of hundreds of megabytes, which RPC nodes reject or time out. SOLTidy's Holder Snapshot reports that the mint has too many token accounts for a browser snapshot instead of hanging. For tokens of that size, use Dune or a paging indexer API that returns token accounts 1,000 at a time.

Tools from this guide

Bookmark SOLTidy

Then open SOLTidy from the bookmark instead of from search results or links in DMs, where look-alike scam sites live.