pumpdata.fun

Find pump.fun wallets worth copying

· · Calls, limits and prices as of 2026-10-22.

How do I find pump.fun wallets worth copying?

Take wallets from the coins that ran, then keep only the ones whose whole record says they take profit. The candidates come from the chain: the holders of yesterday's busiest coins, from any Solana RPC. The record comes from pumpdata.fun: POST /wallets labels each wallet from every pump.fun trade since pump.fun's first day, as winner, net_loser, bot, supply_seller, new or none, with its SOL in and out. Keep the winners, rank them by profit, and check each coin they buy before copying it. The script below does the first two steps in about a minute, free.

The shortlist in Python

Standard library only. python3 shortlist.py prints the shortlist: the winners among the holders that hold long enough to follow. The public Solana RPC rate-limits getTokenLargestAccounts hard, so set RPC to your own (a free Helius or QuickNode endpoint is enough).

# Build a copy-trading shortlist: the wallets holding yesterday's busiest pump.fun coins, kept only if their
# whole pump.fun record says they take profit. Standard library only. Free: pumpdata.fun's /free/ calls
# (30 every five minutes, 2,000 wallet lookups a day) and any Solana RPC.
# python3 shortlist.py    (RPC=<your RPC URL> python3 shortlist.py with your own)
import json, os, time, urllib.error, urllib.request

PD = "https://pumpdata.fun"
RPC = os.environ.get("RPC", "https://api.mainnet-beta.solana.com")  # your own RPC is better: the public one rate-limits
SYSTEM = "11111111111111111111111111111111"   # a wallet's account is owned by the System Program
KEY = None                                    # a free key (https://pumpdata.fun/keys): 2,000 wallets a day of its own

def http(url, body=None, headers=None):
    req = urllib.request.Request(url, json.dumps(body).encode() if body is not None else None,
                                 {"content-type": "application/json", **(headers or {})})
    with urllib.request.urlopen(req, timeout=20) as r:
        return json.load(r)

def rpc(method, *params):
    for wait in (2, 5, 10, 20, 40):            # the public RPC rate-limits; back off and try again
        try:
            return http(RPC, {"jsonrpc": "2.0", "id": 1, "method": method, "params": list(params)})["result"]
        except urllib.error.HTTPError as e:
            if e.code != 429: raise
            time.sleep(wait)
    raise RuntimeError(f"{method}: the RPC kept saying 429; use your own RPC")

def holders(mint):
    """The wallets among a coin's 20 largest holders (pools and the curve are left out)."""
    accts = [a["address"] for a in rpc("getTokenLargestAccounts", mint)["value"]]
    infos = rpc("getMultipleAccounts", accts, {"encoding": "jsonParsed"})["value"]
    owners = list({i["data"]["parsed"]["info"]["owner"] for i in infos if i})
    kinds = rpc("getMultipleAccounts", owners, {"encoding": "base64", "dataSlice": {"offset": 0, "length": 0}})["value"]
    return [o for o, k in zip(owners, kinds) if k and k["owner"] == SYSTEM]

def classify(wallets):
    """Each wallet's record since pump.fun's first day, 50 at a time."""
    out = {}
    path, headers = ("/wallets", {"x-api-key": KEY}) if KEY else ("/free/wallets", {})
    i = 0
    while i < len(wallets):
        try:
            out.update(http(PD + path, {"addresses": wallets[i:i + 50]}, headers))
            i += 50
        except urllib.error.HTTPError as e:
            if e.code == 429:          # the five minutes are used: wait them out, then the same batch
                time.sleep(json.load(e).get("retry_after_s", 60)); continue
            if e.code == 402:          # the day's free wallets are used: free_tier says what next
                print(json.load(e).get("free_tier")); break
            raise
    return out

def shortlist(top=10, min_pnl=50, min_hold=120):
    day = http(PD + "/stats.json")
    coins = day["coins"]["top_volume"][:top]
    wallets = sorted({w for c in coins for w in holders(c["mint"])})
    records = classify(wallets)
    # winners only, and only ones a copier can follow: a median hold of seconds is gone before a copy lands
    keep = [(w, r) for w, r in records.items()
            if r["class"] == "winner" and r["pnl_sol"] >= min_pnl
            and ((r.get("recent") or {}).get("median_hold_s") or 0) >= min_hold]
    keep.sort(key=lambda wr: -wr[1]["pnl_sol"])
    return day["day"], len(wallets), keep

if __name__ == "__main__":
    day, seen, keep = shortlist()
    print(f"{day}: {seen} holder wallets checked, {len(keep)} kept")
    for w, r in keep:
        rc = r.get("recent") or {}
        print(f'{w}  {r["pnl_sol"]:>9,.1f} SOL over {r["coins"]:>5} coins, holds {rc.get("median_hold_s") or 0:>5.0f} s, {rc.get("trades_per_day") or 0:>5.0f} trades a day')

What a run looks like

Run on 2026-10-22 for the day before: the 20 largest holders of each of the ten busiest coins, 168 wallets once pools and the bonding curve are left out, classified in four /free/wallets calls. Ten were winners with at least 50 SOL of profit and a median hold of two minutes or more:

2026-10-21: 168 holder wallets checked, 10 kept
GgbC…ymsT    6,135.5 SOL over 14547 coins, holds   699 s,   252 trades a day
6vbr…paFa    5,483.3 SOL over   168 coins, holds 36832 s,     5 trades a day
2EYV…ay6a    5,453.0 SOL over   663 coins, holds  1193 s,    36 trades a day
G3Vp…X9uk      878.2 SOL over    36 coins, holds  4489 s,    16 trades a day
Ew58…eoNP      774.4 SOL over   464 coins, holds   865 s,    69 trades a day
29HF…YGHG      557.0 SOL over  1515 coins, holds   152 s,    24 trades a day
9M2X…YDzL      400.8 SOL over    59 coins, holds 23479 s,     3 trades a day
45xU…P9UB      359.4 SOL over  1472 coins, holds   332 s,    48 trades a day
BwPF…NpDd      303.8 SOL over  9825 coins, holds   223 s,    54 trades a day
AH39…SKfL      292.2 SOL over   207 coins, holds  5313 s,    13 trades a day

The other 158 were wallets no rule fits, new wallets, bots, supply sellers, or winners that sell within two minutes. That is the point of the filter: holding yesterday's busiest coin is common, and a record of taking profit over dozens of coins is not.

What the labels mean

ClassThe ruleFor copying
winnerTook out 10+ SOL and 1% more than it put in, over 20+ coin-weeks, however much it tradesThe shortlist, if you can follow it
net_loserPut in 50+ SOL more than it took out, over 20+ coin-weeksSkip, however hot it looks today: it loses over time
botA sniper (20+ coins bought in their create block, half or more of its new coins) or 1,500+ trades a day, over the last 20 daysSkip: it is in the block before you
supply_sellerTook out 1.5x what it put in, 100 SOL or moreSkip: its profit is what its buyers paid
newNo record before todayNothing to go on
noneNo rule fits (most wallets)Nothing to go on

Each wallet also comes back with pnl_sol, put_in, took_out, coins, first_seen, and recent: over the last 20 days its trades_per_day, median_hold_s (first buy to first sell), fast_share (trades under 2 s after the last) and snipe_share. The class says whether a wallet makes money; recent says whether you can follow it. Kolscan's top 50 traders on Oct 22: 42 winners, with median holds from 11 seconds to a few minutes.

After the shortlist

Watch the wallets. pumpdata.fun is not a feed. Your RPC's websocket (logsSubscribe with mentions set to a wallet) or a provider's webhooks tell the agent when a shortlisted wallet buys.

Check the coin before copying. A good wallet still buys bad coins, and a copier is always later. Before following a buy, GET /free/launch/{mint} says who launched it, how much the first ten seconds took and how launches like it went: the pre-buy check is that step in code.

Test the copying before the money. Take the shortlist's past buys as picks and replay them: POST /replay fills each one at the pool as it stood after your latency, with every fee and the dead coins counted. Copying a wallet three seconds late is a different trade from the wallet's own.

What it costs

LaneWallet lookupsPrice
No key, /free/wallets2,000 a day per IP; 30 units every five minutes (a 50-wallet call is 5)Free
Free key, x-api-key2,000 a day of its ownFree (sign up)
Per call, x402Any number$0.01 for up to 50 wallets, USDC on Solana, no account
Monthly, POST /keys/upgrade50,000 a day for 30 days$10 in USDC, paid once over x402
The whole tableEvery wallet's record as filesBulk data

Past a free lane's day the call answers 402: the price, and in its body what was used and when it resets. A copy-trading agent already holds a funded wallet, so paying the call is a few lines.

A prompt for agents

Give this to the agent that builds the shortlist.

You are building a copy-trading shortlist of pump.fun wallets with pumpdata.fun.
The docs are at https://pumpdata.fun/llms.txt; read them first.
1. Candidates: take wallets from the chain (the largest holders or the early buyers of coins that ran; the day's
   busiest coins are in https://pumpdata.fun/stats.json, coins.top_volume). pumpdata.fun does not list a coin's
   holders; your RPC does.
2. POST https://pumpdata.fun/free/wallets with {"addresses": [up to 50]} for each batch: each wallet's class
   (winner, bot, supply_seller, net_loser, new, none), pnl_sol, put_in, took_out, coins and first_seen, over every pump.fun
   trade since day one. A 50-wallet call counts five of the 30 free units every five minutes; on 429 wait
   retry_after_s. Keyless wallet lookups are 2,000 a day; a free key (x-api-key) has 2,000 of its own.
3. Keep class "winner" only. Drop "bot" (a sniper or a 1,500-trades-a-day machine: it is in the block before you),
   "supply_seller" (it sells into buyers like you), "net_loser" (it loses over time, however hot today) and "new"
   (no record). Then read "recent": drop winners whose
   median_hold_s is under two minutes (gone before a copy lands) and rank the rest by pnl_sol.
4. On a 402, the free wallets are used: report free_tier from the body (what was used, when it resets, the price,
   the $10 monthly plan) and stop. Never pay without the user's say-so.
5. Before copying any buy, run GET https://pumpdata.fun/free/launch/<mint>: a shortlisted wallet buying a serial
   launcher's bundled coin is still a bad copy.
Never call a wallet safe or a sure thing: the labels are rules over its past, not a forecast.

Questions

Where do the candidate wallets come from?

From the chain. pumpdata.fun does not list a coin's holders or buyers; any Solana RPC does (getTokenLargestAccounts gives a coin's 20 largest holders), and a trade stream such as PumpPortal gives the buyers of every new coin as they buy. The script takes the holders of yesterday's ten busiest coins from /stats.json. pumpdata.fun's part is the next step: what each of those wallets has done on pump.fun since its first day.

What makes a wallet a winner?

A rule, not an opinion: it took out at least 10 SOL and 1% more than it put in, over 20 or more coin-weeks of trading on pump.fun, however much it trades. A net_loser put in 50+ SOL more than it took out over as long: often a wallet on today's leaderboard. A bot, over the last 20 days, bought 20+ coins in their create block (half or more of the coins it bought new), or traded 1,500+ times a day. A supply seller took out 1.5 times what it put in, 100 SOL or more. New means no record before today; none means no rule fits, which is most wallets. Of kolscan's top 50 traders on Oct 22, 42 were winners, 7 none and 1 a bot.

Why not just rank wallets by profit?

Because the top of a profit list is bots and supply sellers. A sniper bot that buys every launch shows a profit you cannot copy (it is in the block before you), and a supply seller's profit is what its buyers paid; copying its buys means buying what it is about to sell you. The class removes both before the ranking.

How many wallets is it free for?

2,000 wallet lookups a day with no key (a 50-wallet call counts five of the 30 free units every five minutes), and 2,000 more a day with a free key. The run on this page checked 170 in four calls. Past that, $0.01 for up to 50 wallets, paid per call in USDC over x402, or $10 a month in USDC on the key for 50,000 a day. The whole wallet table is bulk data.

Why filter on the median hold?

Because a winner you cannot follow is not a copy trade. Each wallet comes back with recent: its trades a day, its median seconds from first buy to first sell, and the share of its trades under 2 seconds apart, over the last 20 days. Kolscan's top traders hold for a median of 11 to 150 seconds; a copier a few seconds late buys their exit. The script keeps winners that hold two minutes or more; set min_hold to taste.

How fresh are the labels?

As of the latest tables build, which every answer names in freshness.data_as_of (and the X-Data-As-Of header). A wallet's class changes slowly, so a shortlist built today stays useful for days. Re-run it weekly.

Does pumpdata.fun copy the trades for me?

No. It answers who is worth copying and whether a coin they buy is worth following. Watching the wallets (your RPC's websocket, or a provider's webhooks) and placing the trades are the agent's own; so is the decision.

The labels are rules over a wallet's past on pump.fun, not a forecast and not advice. The shapes, for a model: llms.txt and openapi.json. The MCP server: https://pumpdata.fun/mcp.