Get token holders and concentration on pump.fun via API

· 8 min read

holdersconcentrationrug checkpositionstoken analysis

A holder list that shows addresses and balances answers the least interesting question. The questions that actually decide trades are: how concentrated is the supply, at what price the biggest wallets entered, and whether they are sitting on profit or trapped. This is how to get token holders on Solana done properly — token holder analysis in two calls on the Raiden API: one fast aggregate for the rug math, one full top-holders list where every row is a complete position.

The number that matters first: /holder-stats

Before you look at any individual wallet, you want one aggregate: how many holders, and how much of the held supply the top of the book controls. That is a single fast call:

curl -H "X-API-Key: $RAIDEN_KEY" \
  "https://terminal.raiden.wtf/api/tokens/9xQm…pump/holder-stats"

{
  "holders": 1342,
  "held": "873421889999000",
  "top10": "412889123456000",
  "top25": "588123987654000"
}

held is the total supply currently in holders' hands; top10 and top25 are the amounts held by the ten and twenty-five largest wallets (raw token units, as strings). Concentration is a division you do yourself: top10 / held — here 47.3% of the held supply in ten wallets, 67.3% in twenty-five. That ratio is the number that matters for rug math: when a handful of wallets control most of what is held, their coordinated exit is the chart, and no amount of volume changes that.

Denominator: divide by held from the same response, never by an assumed total supply. Effective supply differs per token — part of it sits in the bonding curve or the pool — and the API indexes pump.fun and PumpSwap tokens with different supply profiles. held is always the correct base.

Who holds — and at what price: /holders

The full list is where this endpoint stops being a block-explorer clone. Holders come back ordered by balance, and each row is not an address with a number — it is the wallet's entire position on the token:

curl -H "X-API-Key: $RAIDEN_KEY" \
  "https://terminal.raiden.wtf/api/tokens/9xQm…pump/holders?limit=20"

{
  "data": [
    { "trader": "8psN…VRtf", "mint": "9xQm…pump",
      "symbol": "QMASC", "name": "Quantum Mascot",
      "token_balance": "48200000000000",
      "sol_invested": "44800000000", "sol_received": "5500000000",
      "tokens_bought": "61400000000000", "tokens_sold": "13200000000000",
      "realized_pnl": "-4100000000",
      "avg_cost": "0.00073", "current_price": "0.00042",
      "first_trade_at": "2026-07-21T09:41:14Z",
      "last_trade_at": "2026-07-24T07:03:55Z", "trade_count": 7 },
    { "trader": "Cb3f…g9rE", "mint": "9xQm…pump",
      "symbol": "QMASC", "name": "Quantum Mascot",
      "token_balance": "43600000000000",
      "sol_invested": "6910000000", "sol_received": "7700000000",
      "tokens_bought": "62800000000000", "tokens_sold": "19200000000000",
      "realized_pnl": "5450000000",
      "avg_cost": "0.00011", "current_price": "0.00042",
      "first_trade_at": "2026-07-21T09:41:12Z",
      "last_trade_at": "2026-07-23T22:12:03Z", "trade_count": 12 },
    …
  ]
}

Read the two rows as a story. The largest holder bought at an average cost of 0.00073 and the price is 0.00042: underwater, already down 4.1 SOL realized (realized_pnl is net of all fees — swap fees, creator fees, tips, priority fees). That wallet is a bagholder: it is not defending the price, it is waiting for an exit. The second-largest entered at 0.00011 — nearly 4× in unrealized profit, with 5.45 SOL already banked. That wallet is latent sell pressure: every uptick makes its exit more attractive. Same balances, opposite implications — and invisible in a holders list that only shows amounts.

The rest of the position object rounds out the picture: sol_invested / sol_received (lamport strings; 1 SOL = 1e9), avg_cost and current_price in the same unit (lamports per raw token unit, so the comparison is direct), tokens_bought / tokens_sold, trade_count for churn, and first_trade_at to separate launch-slot entries from late buyers — the same timestamp trail behind finding who bought first on a launch.

Reading the distribution: healthy vs captured

Patterns that separate an organic book from a staged one:

Pattern in the dataReading
top25 clearly larger than top10, holder count growing while top-10 share fallsDepth beyond the leaders — supply is actually distributing
top10top25heldBeyond the top ten nobody holds anything meaningful; the holder count is cosmetic dust
High top-10 share from the very first slotsSupply captured at launch, not accumulated — check the launch for bundling
Top holders all deep in profit (avg_cost far below current_price)Latent sell pressure — early wallets waiting for exit liquidity
Top holders mostly underwaterBagholders; the wallets that could support the price already spent their ammunition

The third row deserves its own tooling. When concentration exists from second one, it was not earned on the open market: the dev or an operator bought a chunk of supply inside the creation slot across coordinated wallets. GET /tokens/{mint}/launch quantifies exactly that — bundled_pct_supply, dev_bundle_pct_supply and the reconstructed bundle groups — and the full walkthrough is in the pump.fun bundle checker guide. The Raiden Trust score folds the same signal in: heavy bundling is one of the hard signals that caps the score (capped: true, cap_reason: "bundled") no matter how good everything else looks.

Python: a holder concentration checker in 25 lines

Concentration snapshot plus a top-holders API PnL table, for any mint:

import requests

BASE = "https://terminal.raiden.wtf/api"
H = {"X-API-Key": "YOUR_KEY"}
MINT = "9xQm…pump"
LAMPORTS = 1e9

# 1 · concentration snapshot — divide by held, never by an assumed supply
hs = requests.get(f"{BASE}/tokens/{MINT}/holder-stats", headers=H).json()
held = int(hs["held"])
print(f"holders: {hs['holders']}")
print(f"top-10:  {int(hs['top10']) / held:.1%} of held supply")
print(f"top-25:  {int(hs['top25']) / held:.1%} of held supply")

# 2 · top-holder table: who holds, at what cost, in profit or underwater
rows = requests.get(f"{BASE}/tokens/{MINT}/holders",
                    params={"limit": 20}, headers=H).json()["data"]
for r in rows:
    share    = int(r["token_balance"]) / held
    invested = int(r["sol_invested"]) / LAMPORTS
    realized = int(r["realized_pnl"]) / LAMPORTS
    cost, px = float(r["avg_cost"]), float(r["current_price"])
    state = "-" if px == 0 else ("in profit" if px > cost else "underwater")
    print(f"{r['trader'][:6]}  share={share:6.2%}  invested={invested:8.2f} SOL  "
          f"realized={realized:+8.2f} SOL  trades={r['trade_count']:3}  {state}")

Three lines of output tell you more than most dashboards: a token where the top-10 share is high and every top row prints in profit is a coiled spring; the same share with rows printing underwater is a graveyard. The share column uses held as its base, so the script works unchanged on any indexed pump.fun or PumpSwap token regardless of its supply profile.

Watching holders change: page the tape, don't re-poll

Polling /holders in a loop to detect accumulation is the wrong tool: you re-download the whole book to discover one change, and you can't see what happened between two snapshots. Holders are built from trading positions, so every change to the list comes from a swap — and the tape is pageable with a cursor:

# every swap is a balance delta: +token_amount on buys, -token_amount on sells
cursor, balances = None, {}
p = {"order": "asc", "limit": 200}
while True:
    if cursor: p["cursor"] = cursor
    page = requests.get(f"{BASE}/tokens/{MINT}/swaps", params=p, headers=H).json()
    for s in page["data"]:
        delta = int(s["token_amount"]) * (1 if s["is_buy"] else -1)
        balances[s["trader"]] = balances.get(s["trader"], 0) + delta
    if not page.get("next_cursor"): break
    cursor = page["next_cursor"]

With order=asc and the cursor you replay the token's whole trading history once and then keep the map current by polling only for new rows — each swap row carries trader, is_buy, token_amount, sol_amount, price, slot and block_index, so the same loop doubles as an accumulation/distribution detector with exact ordering. For push instead of pull, swap events also arrive over a WebSocket stream — streams are scoped to a wallet list rather than a mint, so put the holders you care about on a list and get their swaps pushed in real time.

Coverage: the API keeps a 6-month rolling window with a complete record since May 1, 2026. Holder stats and holder positions are computed live from that record — amounts come back as strings in lamports (1 SOL = 1e9) for precision, with token amounts in raw token units.

One last shortcut: GET /tokens/{mint}/pack returns holder_stats alongside the launch analysis, trust score, smart-money and funding-cluster views in a single dossier call — the whole due-diligence fan-out in one request. The exact shapes for every endpoint above, with runnable examples, are in the interactive API docs.

FAQ

Frequently asked questions

How do I get the top holders of a pump.fun token via API?

GET /tokens/{mint}/holders returns holders ordered by token balance. Each row is a full position object: balance, SOL invested and received, tokens bought and sold, realized PnL net of all fees, average cost, current price, first/last trade timestamps and trade count — so you see not just who holds, but at what price.

How do I check holder concentration on a Solana token?

GET /tokens/{mint}/holder-stats returns the holder count plus the supply held by the top 10 and top 25 holders in one fast aggregate call. Divide top10 or top25 by the held field from the same response to get concentration percentages without assuming any total supply.

Why shouldn't I compute concentration against a 1 billion supply?

Effective supply differs across tokens — part of it sits in the bonding curve or the pool, and not every token is minted the same. The held field in holder-stats is the actual holder-held denominator, so the ratio stays correct for any indexed pump.fun or PumpSwap token.

Can I tell whether top holders are in profit or underwater?

Yes. Every holder row carries avg_cost and current_price: an average cost far below the current price means the holder sits on unrealized profit — latent sell pressure — while avg_cost above the price marks a bagholder. realized_pnl, net of all fees, shows what they already took off the table.

How do I track holder changes in real time?

Don't re-poll the holders list. Holders are built from trading positions, so every change to the list comes from a swap: page the tape with GET /tokens/{mint}/swaps using order=asc and the cursor, and apply each swap's token_amount as a balance delta per trader.

How far back does the holder and swap data go?

The API keeps a 6-month rolling window with a complete record since May 1, 2026. Holder stats and holder positions are computed live from that record for any indexed pump.fun / PumpSwap token.

Build on the same data

Private beta — free invite-only keys, full REST + WebSocket access, and the same firehose that powers the Terminal.