How to build a pump.fun and fomo copy trading bot on a social trading API

Pick traders worth copying from pump.fun and fomo.family data, watch them live, filter their trades and size your own. A working blueprint with TypeScript code.

6 min read

A pump.fun and fomo copy trading bot: find traders, vet them, watch them live
On this page
  1. The pipeline
  2. 1. Find candidates
  3. 2. Vet them
  4. 3. Watch them live
  5. 4. Decide what to copy
  6. 5. Execute (outside this API)
  7. 6. Review, and drop who isn't working
  8. What it costs
  9. Frequently asked questions
  10. Where to go next

A copy trading bot looks simple: when a good trader buys, you buy; when they sell, you sell. Most pump.fun copy trading bots lose money anyway, and rarely because of code. They copy the wrong people, they copy trades they can't get the same price on, and they keep holding after the trader they copied has left.

This guide builds a pump.fun and fomo.family copy trading bot around those three problems. It uses social trading data (leaderboards, PnL, callouts and how followers did) to choose whom to follow. It watches those traders live and filters which of their trades are worth copying. It does not place trades for you: pumpfomo is a read-only data API, and execution stays with your own wallet and keys. The code is TypeScript.

The pipeline

  1. Find candidates on pump.fun's and fomo.family's leaderboards.
  2. Vet them with PnL breakdowns, trading stats and callout records.
  3. Watch them live over one WebSocket.
  4. Decide which of their trades to copy, with filters you can explain.
  5. Execute with your own wallet, outside this API.
  6. Review: compare your fills with theirs and drop who isn't working.

1. Find candidates

Both platforms rank traders by PnL, and both rankings are a starting list, not an answer.

ts
const BASE = "https://api.pumpfomo.com"
const headers = { Authorization: `Bearer ${process.env.PUMPFOMO_KEY}` }
const get = async (path: string) => {
  const r = await fetch(BASE + path, { headers })
  if (!r.ok) throw new Error(`${path}: ${r.status}`)
  return (await r.json()).data
}

// pump.fun: top 100 by realized PnL this month
const pump = await get("/v1/pump/leaderboard?period=monthly&sort=realized")
// fomo.family: top traders over 30 days
const fomo = await get("/v1/fomo/leaderboard/30d")

const candidates = [
  ...pump.entries.map((e) => ({ platform: "pump", id: e.userId, wallet: e.walletAddress, name: e.username,
    realized: e.realizedPnlUsd, unrealized: e.unrealizedPnlUsd })),
  ...fomo.leaderboard.map((t) => ({ platform: "fomo", id: t.id, handle: t.userHandle, name: t.displayName,
    pnl: t.pnl30d, trades: t.numTrades })),
]

Two leaderboard calls cost 2 credits. Notice sort=realized: pump.fun's leaderboard can also sort by combined or unrealized PnL. On a weekly board, the top spot is often one open bag that has run, which is luck you can no longer copy.

Other places to look:

  • Leaderboard positions: the single best positions behind the pump.fun board.
  • Streak leaderboard: wallets that have traded every day for the longest. They're active, but not necessarily good.
  • Clan leaderboard: fomo.family groups ranked by combined PnL, with their members.
  • Callouts: pump.fun traders who publish calls, which you can score directly (next section).

2. Vet them

Here is what separates a trader worth copying from a lucky week, and where to read each signal:

Question Signal Endpoint
Is the profit real or on paper? Realized vs. unrealized PnL /v1/pump/users/{wallet}/pnl, /v1/fomo/users/{handle}/trades
Is it one trade or many? Trade count, biggest trade, volume over 1, 7 and 30 days /v1/pump/users/{wallet}/trader-stats
Is it consistent? The equity curve over 30 days /pnl/chart, /pnl-chart
Can you keep up? Average hold time /v1/fomo/users/{handle} (averageHoldTimeSeconds)
Did following them pay? Callout hit rates and followers' PnL /v1/pump/users/{wallet}/callout-stats

Hold time decides whether copying is even possible. Your copy lands seconds after their trade. A trader who holds for two days gives you room. A trader who flips in 40 seconds is gone by the time you're in, and your buy is part of what they sell into.

Callout stats are the closest thing to a track record. pump.fun measures each trader's callouts: how many reached 1.2x, 1.5x and 2x, the median multiple, and how long the peak took. It also measures the PnL of the people who bought after the call (pump.fun calls them "tail") at 1 hour, 4 hours and 24 hours. That last number answers your question directly: did following this person make money?

ts
async function vetPump(wallet: string) {
  const [pnl, stats, calls] = await Promise.all([
    get(`/v1/pump/users/${wallet}/pnl?period=1m`),
    get(`/v1/pump/users/${wallet}/trader-stats`),
    get(`/v1/pump/users/${wallet}/callout-stats`),
  ])
  const m = calls.monthly
  return {
    realizedShare: pnl.realizedPnl.usd / Math.max(1, Math.abs(pnl.pnl.usd)),
    trades30d: stats.window30d.trades,
    biggestTradeShare: stats.biggestTradeUsd / Math.max(1, stats.allTimeVolumeUsd),
    callouts: m.totalCallouts,
    hit2x: m.twoXPercent,
    medianMultiple: m.medianMultiple,
    followersPnl4h: m.tailPnl4hUsd, // what people who bought after their calls made in 4h
  }
}

That's 5 + 1 + 2 = 8 credits per trader. Vetting 200 candidates once a week costs 1,600 credits.

A simple rule set that removes most bad picks:

  • At least 30 trades in 30 days, so there's a sample to judge.
  • Realized PnL is most of total PnL.
  • No single trade above about a fifth of their volume.
  • For callers: at least 10 calls a month and a positive tailPnl4hUsd.
  • Average hold time of an hour or more, unless you're running very fast infrastructure.

3. Watch them live

Follow your shortlist on one connection. pump.fun traders go in by user id, which follows every wallet we know for them. fomo.family traders go in by handle (on fomo, the profile address isn't the trading wallet, so addresses wouldn't work anyway).

ts
import WebSocket from "ws"

type Signal = { platform: "pump" | "fomo"; who: string; side: "buy" | "sell"; token: string;
  chain: string; usd: number; mcap?: number; ts: number }

function watch(pumpIds: string[], fomoHandles: string[], onSignal: (s: Signal) => void) {
  const ws = new WebSocket("wss://api.pumpfomo.com/v1/stream", { headers })
  ws.on("open", () => ws.send(JSON.stringify({
    op: "subscribe", channel: "traders", pump: pumpIds, fomo: fomoHandles, replay: false,
  })))
  ws.on("message", (raw) => {
    const m = JSON.parse(raw.toString())
    if (m.type === "trade") onSignal({ platform: "pump", who: m.trader?.userId ?? m.wallet, side: m.side,
      token: m.mint, chain: m.chain, usd: m.amountUsd, mcap: m.marketCapUsd, ts: m.ts })
    if (m.type === "fomo_trade" && m.kind !== "thesis") onSignal({ platform: "fomo", who: m.trader.handle,
      side: m.kind, token: m.token.address, chain: m.token.chain, usd: m.amountUsd, mcap: m.marketCapUsd, ts: m.ts })
  })
  ws.on("close", () => setTimeout(() => watch(pumpIds, fomoHandles, onSignal), 2000))
}

A connection takes up to 1,000 pump.fun traders and 100 fomo handles. Streams come with the plan from Builder up. The WebSocket guide covers reconnects and replay in depth.

4. Decide what to copy

Most of a copy trading bot's edge lives in the trades it skips. Filters worth having, all cheap to check:

ts
const MAX_LAG_MS = 8_000
const held = new Map<string, number>() // token -> our size in USD

async function shouldCopy(s: Signal): Promise<number> {
  if (Date.now() - s.ts > MAX_LAG_MS) return 0            // too late: the price has moved
  if (s.side === "sell") return held.get(s.token) ?? 0     // mirror exits, always
  if (s.usd < 200) return 0                                // a small buy is a test, not a conviction
  if (held.has(s.token)) return 0                          // one entry per token
  if (s.mcap && (s.mcap < 30_000 || s.mcap > 50_000_000)) return 0

  if (s.platform === "fomo") {
    const w = await get(`/v1/fomo/tokens/${s.chain}/${s.token}/warnings`)
    if (w.disableSelling || w.disableBuying) return 0      // can't get out: don't get in
  } else {
    const a = await get(`/v1/pump/coins/${s.token}/market-activity`)
    if (a["1h"].numBuyers < 15) return 0                   // too thin to exit
  }
  return Math.min(100, s.usd * 0.05)                       // size: a fraction of theirs, capped
}

Token warnings also lists each warning fomo.family shows before a trade, with its severity, if you want to be stricter. The ideas behind the filters:

  • Lag kills. If the signal is more than a few seconds old, the price you'd pay isn't the price they paid.
  • Size relative to conviction. A trader's $50 buy is often a probe. Copy the trades they put money behind.
  • Liquidity before entry. If a coin has a handful of buyers in the last hour, you'll be selling into nobody.
  • Mirror the exit, always. The most expensive copy-trading bug is copying the buy and missing the sell.
  • Cap everything: per trade, per token, per day.

5. Execute (outside this API)

pumpfomo doesn't trade, by design: it never holds keys or signs anything. Your bot sends its own transactions:

  • Solana: pump.fun coins still on the bonding curve trade through pump.fun's program; graduated ones trade on PumpSwap and other pools. An aggregator like Jupiter routes across them for you.
  • EVM chains: a DEX router or an aggregator on the coin's chain.

Keep the signing key in its own process, set slippage limits, and pay a priority fee on Solana when blocks are busy. Log the price you got next to the price the trader got.

6. Review, and drop who isn't working

Once a week, compare:

  • Your realized PnL per copied trader (your own wallet's PnL works for this on pump.fun).
  • Their PnL over the same window.
  • Your average entry against theirs. That gap is your real cost of copying.

Drop traders whose copies lose even when they win. It means they're too fast, or their wins come from trades you can't reproduce.

What it costs

Piece Credits Per month
Leaderboards, daily 2 a day ~60
Vetting 200 traders, weekly 1,600 a week ~6,900
One stream, always on included 0
Pre-trade checks, 300 a day 1 to 2 each ~18,000
Total ~25,000

That fits in the Builder plan ($149 a month, 500,000 credits), the first with a stream, many times over. See pricing.

Frequently asked questions

What is a pump.fun copy trading bot?

It's a program that watches chosen pump.fun traders and places similar trades from your own wallet when they trade. The hard parts are choosing whom to copy, skipping trades you can't reproduce, and always mirroring exits.

Can I copy fomo.family traders?

Yes. Follow their fomo handles on the stream's traders channel and you'll get each buy and sell as it happens, with token, size, price and market cap. Execute on the same chain with your own wallet.

Does pumpfomo execute trades?

No. pumpfomo is a read-only data API. It supplies the signals (leaderboards, PnL, callouts, live trades), and your bot executes with its own keys.

Which traders should I copy?

Traders with a sample of trades, mostly realized profit, no single trade carrying the record, and hold times long enough for you to follow. For pump.fun callers, check that the people who bought after their calls made money (tailPnl4hUsd and tailPnl24hUsd in callout stats).

How fast will my bot be?

Trades reach the stream within seconds of the trader's own. Add your filtering and your transaction's time to land. Plan for a few seconds of lag, and copy traders whose positions last much longer than that.

Is copy trading on pump.fun profitable?

Sometimes, and less often than leaderboards suggest. The leader buys first and you buy after, often from the same thin pool. Test on paper, keep sizes small and measure your fills against theirs.

Where to go next

Try it on real data, free.

10,000 credits a month, wallet or Telegram sign-in, no card. Every endpoint in these guides works on the Free plan; streams start at Builder.

More guides