Home / Blog / Use Cases

Use Cases

Subscribe to One Trader's Trades in Real Time: a WebSocket for Copy-Trading

How to subscribe to a single trader’s live trades over a per-trader WebSocket, with a working JavaScript example, the message shapes, runtime subscribe and unsubscribe, and the REST endpoints that complete a copy-trading pipeline.

Copy-trading has one hard requirement that sits above everything else: the instant a specific trader makes a move, you need to know. Not thirty seconds later. Not after your next polling cycle. The moment it happens.

Most integrations get this wrong in one of two directions. They either poll an endpoint every few seconds, which is slow and wastes requests on the many intervals where nothing changed, or they connect to a full activity firehose and then filter client-side, which means you receive and parse thousands of events you do not care about just to catch the handful that matter. Both work. Neither is the right shape for following one person.

The right shape is a per-trader WebSocket subscription. You open a socket that is already scoped to a single trader, and the server sends you only that trader's events. This post walks through the endpoint we shipped for exactly this, the message shapes, a working JavaScript example, and how the WebSocket fits into the rest of a copy-trading pipeline.

The endpoint

Connect to a single trader's live stream by passing their handle in the query string:

wss://api.fomoapi.io/ws/alerts?trader=frankdegods

From that connection you receive only frankdegods events: buys, sells, and theses, as they happen. Nothing else comes down the pipe. There is no client-side filtering to write and no unrelated traffic to discard.

You can narrow further with additional query parameters, and they combine:

  • ?token= filters to a single token, by symbol or by contract address.
  • ?source= filters by where the event came from, either feed or push.
  • ?type= filters by event kind: buy, sell, thesis, whale, price, or trade.

So wss://api.fomoapi.io/ws/alerts?trader=frankdegods&type=buy gives you only that trader's buys, which is often all a mirroring bot needs to act on.

The message shape

Every alert arrives as a JSON object with a stable shape:

{
  "type": "alert",
  "alertType": "buy",
  "source": "feed",
  "trader": "frankdegods",
  "token": "WIF",
  "tokenAddress": "EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm",
  "chainId": "solana",
  "notificationType": "buy",
  "usdValue": 4200,
  "text": "opened a position in WIF",
  "ts": 1756400000000
}

The fields you will lean on most are alertType (or notificationType) to branch your logic, token and tokenAddress to identify what was traded, usdValue to size your own action proportionally, and ts for the event timestamp. The text field carries the human-readable description, which is useful for logging and for thesis events where the reasoning is the payload.

A working example

Here is a complete client that opens a scoped socket, handles the welcome message, and acts on buy alerts. It uses the ws package in Node, but the browser WebSocket API is identical apart from construction.

import WebSocket from 'ws';

const trader = 'frankdegods';
const ws = new WebSocket(`wss://api.fomoapi.io/ws/alerts?trader=${trader}`);

ws.on('open', () => {
  console.log(`listening for ${trader}`);
});

ws.on('message', (raw) => {
  const msg = JSON.parse(raw.toString());

  // Sent once when the connection is established.
  if (msg.type === 'welcome') {
    console.log('connected', msg);
    return;
  }

  if (msg.type !== 'alert') return;

  if (msg.alertType === 'buy') {
    onBuy(msg);
  } else if (msg.alertType === 'sell') {
    onSell(msg);
  } else if (msg.alertType === 'thesis') {
    console.log('thesis:', msg.text);
  }
});

ws.on('close', () => {
  console.log('socket closed, reconnect here');
});

function onBuy(a) {
  // This is where your copy-trading logic runs.
  console.log(`${a.trader} bought ${a.token} for $${a.usdValue}`);
  // e.g. place your own proportional order, or queue for review.
}

function onSell(a) {
  console.log(`${a.trader} sold ${a.token}`);
}

That is the whole loop. Open, read, branch on alertType, act. Because the socket is already scoped to one trader, the onBuy handler never fires for anyone else.

Subscribing at runtime

You do not have to open a new connection for every trader you want to follow. A single socket can change who it is watching without reconnecting. Send a subscribe message over the open connection:

ws.send(JSON.stringify({ action: 'subscribe', trader: 'frankdegods' }));

The server confirms with:

{ "type": "subscribed", "filter": { "trader": "frankdegods" } }

After that confirmation the socket streams only frankdegods events. One useful detail: on subscribe you also receive an immediate replay of that trader's recent matching alerts, so a freshly connected client is not blind to what happened in the seconds before it subscribed. That replay means you can start a follower mid-session and still see the trader's latest activity.

To stop scoping and return to the full firehose, send:

ws.send(JSON.stringify({ action: 'unsubscribe' }));

This runtime control is what lets you build a bot that follows a rotating set of traders, or one that lets a user pick who to mirror from a dropdown, all over one long-lived connection.

The rest of the pipeline

The WebSocket answers when and what. A real copy-trading system needs more than that, and the REST API fills in the gaps.

  • GET /v2/users/{handle} returns the trader's real, resolved on-chain wallet across Solana and EVM. This is what lets you verify a move on-chain, size against their actual holdings, or mirror the trade directly on-chain rather than through your own venue.
  • GET /v2/thesis/user/{id} returns the trader's stated reasoning. Copying a buy without the thesis is copying blind. With it you can apply judgment: skip the ones whose reasoning you disagree with, size up the ones that match your own read.
  • GET /v2/users/{handle}/trades gives their trade history, so you can study a trader before you follow them and confirm the live stream matches their track record.
  • GET /v2/users/{handle}/balances gives their current holdings, useful for proportional sizing and for knowing when a position has actually been closed.

Put together, the flow is: the per-trader WebSocket tells you the instant a trade happens and what it was, the user endpoint gives you the wallet to verify or mirror it on-chain, the thesis endpoint gives you the reasoning to copy with judgment rather than blindly, and the trades and balances endpoints give you the history and current state to size correctly.

Why the PnL can be trusted

One reason this data is worth building on: the profit and loss figures are computed from real on-chain trades, not from anything a trader types into a profile. A wallet's buys and sells are public and immutable once they settle. Because the numbers are derived from that on-chain activity, a trader cannot inflate their record by self-reporting a win they did not take. When you are deciding whose trades to mirror, that distinction matters. You are choosing based on what a wallet actually did, not on a claim.

Build it

Everything above is live. Point a socket at wss://api.fomoapi.io/ws/alerts?trader=<handle>, handle the welcome and alert messages, and act on the buys. Add the runtime subscribe and unsubscribe messages when you want one connection to follow many traders. Reach for the REST endpoints when you need the wallet, the thesis, the history, or the holdings.

We give you the stream and the resolved data. You build the copy-trading bot on top of it, with whatever sizing, filtering, and judgment your strategy calls for.

Subscribe to one chain, and get the exact token traded

Every message now carries the token's contract address and chain, not just the ticker symbol. The symbol alone is ambiguous (many coins reuse a ticker); the contract is not.

{
  "type": "alert", "alertType": "buy", "trader": "frankdegods",
  "token": "CATE",
  "tokenAddress": "Ai66LHZG9MCzg1WKdawwqduVAXpNDUuV8M3uyq5ppump",
  "chainId": 1399811149,
  "text": "frankdegods bought $CATE"
}

To stream a single chain, add a chain filter on connect:

new WebSocket("wss://api.fomoapi.io/ws/alerts?chain=solana");
// also: base, eth, bsc, polygon, arbitrum, or a raw chainId

Filters combine, so one trader on one chain is a single socket:

new WebSocket("wss://api.fomoapi.io/ws/alerts?trader=frankdegods&chain=base");

Because each alert includes tokenAddress and chainId, a copy-trading bot can route an order to the exact token on the exact chain without resolving the symbol itself. That removes a whole class of wrong-token mistakes from an automated pipeline.

Ship on verified trader data

Both-chain wallets, real PnL, and a realtime feed. One API.

Get an API key

FAQ

How do I receive only one trader’s trades instead of the whole feed?
Open a WebSocket to wss://api.fomoapi.io/ws/alerts?trader=<handle>. The connection is scoped to that handle, so the server sends only that trader’s buys, sells, and theses. There is no client-side filtering to write and no unrelated traffic to discard.
Can I change which trader I am following without reconnecting?
Yes. Over the open socket, send {"action":"subscribe","trader":"frankdegods"} and the server replies {"type":"subscribed","filter":{"trader":"frankdegods"}}, then streams that trader. On subscribe you also get an immediate replay of the trader’s recent matching alerts. Send {"action":"unsubscribe"} to return to the full firehose.
Why can I trust the PnL numbers when choosing who to copy?
PnL is computed from real on-chain trades, which are public and immutable once they settle. Because the figures are derived from actual wallet activity rather than self-reported, a trader cannot inflate their record with wins they did not take.