Building with Anaxer

How to build Solana whale alerts from wallet transfers

Build Solana whale alerts by subscribing to the Anaxer transfers stream with a minVolumeUsd filter, so only large SOL and token transfers between wallets reach your code, then posting each one to a webhook. Covers token-scoped and network-wide alerts, cutting noise, and following up a flagged wallet.

8 min readAnton Gilborn

Build Solana whale alerts by subscribing to the Anaxer transfers stream with a minVolumeUsd filter and forwarding each event to a webhook. The server does the filtering: only SOL and token transfers between wallets worth at least your threshold reach your code, each already valued in USD. This guide covers the filter, the two alert modes, a runnable alert loop, cutting noise, and following up a flagged wallet.

Who is this guide for?

This guide is for developers building alerting on top of large on-chain movements:

  • Alert bots that post to a chat channel or webhook
  • Token teams and communities watching large holders move
  • Trading dashboards that surface large transfers next to trades
  • Researchers following funds between wallets

If you want the basics of the stream first - the event shape, the wallets and direction filters, reconnect handling - read how to track Solana wallet transfers in real time.

How does the minVolumeUsd filter work?

Every transfer event carries volumeUsd: the USD value of the transfer when it was decoded. minVolumeUsd keeps only events at or above a value, maxVolumeUsd only those at or below, and minVolumeUsd must not exceed maxVolumeUsd.

One rule matters for alerting. volumeUsd is null when the token had no price yet, and a null fails a volume filter. So an unpriced transfer never matches, however many tokens moved. That is usually what you want - an alert with no USD value is not a whale alert - but it means the stream is not a record of every large raw amount.

A transfer here is a wallet sending SOL or a token to a different wallet. SOL appears as the wrapped-SOL mint So11111111111111111111111111111111111111112.

Which mode do I need: token-scoped or network-wide?

There are two ways to scope the subscription, and they map to different plans.

ModeFiltersPlan
Token-scopedmints + minVolumeUsdStarter ($39/mo): up to 25 tokens at once. Pro and Enterprise: up to 100
Network-wideminVolumeUsd onlyPro ($99/mo) and Enterprise

Token-scoped alerts watch large transfers of tokens you name - your own token, or a watchlist:

{
  "type": "subscribe",
  "id": "whales",
  "channel": "transfers",
  "filters": {
    "mints": ["EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm"],
    "minVolumeUsd": 100000
  }
}

Network-wide alerts drop mints and receive every transfer above the threshold:

{
  "type": "subscribe",
  "id": "whales",
  "channel": "transfers",
  "filters": { "minVolumeUsd": 100000 }
}

Starter must send wallets or mints, so the second subscription is rejected with invalid_filters on a Starter key. The free plan does not include transfers. In both modes there is no wallets filter, so each transfer arrives once, with wallet set to the sender. The 100000 here is an example value, not a recommendation.

A third option is a wallet watchlist: add wallets (up to 100 on Starter, 1,000 on Pro) to alert only when specific addresses move size.

How do I build the alert loop?

The loop below subscribes with the TypeScript SDK (@anaxer/sdk 0.2.0 or later), formats a message, and POSTs it as JSON to a webhook URL. It is plain JavaScript with no dependency beyond the SDK. Save it as alerts.mjs, run npm install @anaxer/sdk, set ANAXER_API_KEY and WEBHOOK_URL, and start it with node alerts.mjs (Node 20 or later).

import { connect } from "@anaxer/sdk";

const apiKey = process.env.ANAXER_API_KEY;
const webhookUrl = process.env.WEBHOOK_URL;
if (!apiKey || !webhookUrl) throw new Error("Set ANAXER_API_KEY and WEBHOOK_URL");

const MIN_USD = 100_000;
const MINTS = ["EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm"]; // WIF

const client = connect({ apiKey });
const seen = new Set();
const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});
const short = (address) => `${address.slice(0, 4)}...${address.slice(-4)}`;

const sendAlert = async (t) => {
  const { from, to, mint, symbol, amount, uiAmount } = t.transfer;

  // One alert per transfer, even if it is delivered more than once.
  const key = [t.signature, from, to, mint, amount].join("|");
  if (seen.has(key)) return;
  if (seen.size >= 10_000) seen.clear();
  seen.add(key);

  const text =
    `${usd.format(t.volumeUsd)} transfer: ${uiAmount.toLocaleString("en-US")} ` +
    `${symbol ?? short(mint)} from ${short(from)} to ${short(to)}`;

  const res = await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text,
      volumeUsd: t.volumeUsd,
      from,
      to,
      mint,
      signature: t.signature,
      slot: t.slot,
      timestamp: t.timestamp,
    }),
  });
  if (!res.ok) console.error("webhook failed", res.status);
};

const sub = client.stream("transfers", { mints: MINTS, minVolumeUsd: MIN_USD });

sub.on("subscribed", (filters) => console.log("watching", filters));
sub.on("data", (t) => sendAlert(t).catch((err) => console.error("alert failed", err)));
sub.on("error", (err) => console.error(err.code, err.message));
client.on("error", (err) => console.error(err.code, err.message));

For network-wide alerts on Pro, delete the mints line from the filters. The JSON body is deliberately generic: rename text to whatever field your chat or automation tool expects, or let the receiver build its own message from the structured fields. The SDK reconnects and re-subscribes on its own. In Python the same subscription is client.stream("transfers", mints=[...], min_volume_usd=100_000).

How do I cut the noise?

Much of the usual whale-alert noise is removed by what the stream counts as a transfer:

  • Program accounts are excluded. Moves to program accounts - deposits, payouts, a DEX's pools - are not transfers, so they never trigger an alert.
  • Swaps are excluded. Two wallets exchanging tokens in one transaction is a swap and arrives on the trades stream. Inside a transaction that also swaps, a transfer counts only when it moves at least half of what the sender swapped in that token, and app fees do not count.
  • Unpriced tokens are excluded as soon as you set a volume bound.

What is left is yours to tune:

  • Dedupe. With a wallets filter, a transfer between two watched wallets arrives twice, once per wallet. The loop above keys on the signature and the transfer's from, to, mint and amount, so it alerts once either way. To keep one message per wallet, key on signature + wallet.
  • Key on the mint, not the symbol. symbol is a label: anyone can name a token USDC. For network-wide alerts, either show the mint in the message or restrict the subscription to mints you trust (up to 100 on Pro).
  • Choose the threshold from live data. Subscribe in the API Playground with a few minVolumeUsd values and see how much each lets through before you wire up a channel. Start high and lower it.
  • Tier in code. Subscribe once at your lowest threshold and route by volumeUsd in your handler; it uses one subscription where separate bands would use several.

How do I follow up a flagged wallet?

An alert names two addresses. Two calls tell you what each has been doing.

First, pull the wallet's recent transfers with GET /v1/wallets/:wallet/transfers. It takes direction, mints, minVolumeUsd, maxVolumeUsd, limit, cursor, from and to, and returns the newest first:

const wallet = "5FHwkW5742VKq9W8pN3mL7jR2sT6cX4bY1zD8gH9K2pQ";

const page = await client.wallets.transfers(wallet, {
  direction: "out",
  minVolumeUsd: 10_000,
});

for await (const t of page) {
  console.log(t.transfer.to, t.transfer.uiAmount, t.transfer.symbol, t.volumeUsd);
}

Iterating the page walks every next cursor. An unknown wallet returns 200 with an empty list.

Second, watch what the wallet does next. Its swaps come on the trades stream with a wallets filter, and its further transfers on transfers with the same filter:

client.stream("trades", { wallets: [wallet] }).on("data", (swap) => {
  console.log(swap.swap.from.symbol, "->", swap.swap.to.symbol, swap.volumeUsd);
});

client.stream("transfers", { wallets: [wallet] }).on("data", (t) => {
  console.log(t.transfer.from, "->", t.transfer.to, t.volumeUsd);
});

On Starter, a trades subscription must also carry mints, so a wallets-only trades subscription needs Pro. Subscriptions are also capped per channel (one on Starter, two on Pro), so a second transfers subscription next to the alert loop needs Pro as well; on Starter, use the REST call above. An AI assistant can do the same follow-up through the Anaxer MCP server, which has list_wallet_transfers and tail_stream on transfers.

What are the limits?

Be clear about what this data can and cannot tell you:

  • No wallet labels. Anaxer does not label wallets. It does not know which address is an exchange or a fund, so alerts show addresses. If you need names, keep your own labels and join them to from and to.
  • Seven days of history, from 11 November 2026. Transfers are stored only from the day the feature launched, and REST history covers the last 7 days.
  • USD value at decode time. volumeUsd is the value when the transfer was decoded, and unpriced transfers are invisible to a volume filter.
  • No chain-wide backfill. REST transfers are per wallet. If a network-wide subscription drops, the gap can be recovered only for wallets you query by address.
  • Transfers only. Large swaps are on the trades stream, which also takes minVolumeUsd.

Next steps

Get a key in the console, pick a mode, and point the loop at a test webhook. The transfers stream reference lists every filter and field, the Solana wallet transfers API page covers the product, and pricing shows what each plan includes. To make the connection production-ready, read the Solana WebSocket streams guide.

Frequently asked questions

How do I get alerts for large Solana transfers?

Subscribe to the Anaxer transfers WebSocket stream with a minVolumeUsd filter set to your threshold in USD. The server then sends only SOL and token transfers between wallets worth at least that much, each with the sender, recipient, mint, amount and USD value, and your code posts them to a webhook or chat channel.

Can I build whale alerts for one specific Solana token?

Yes. Add the token's mint address to the mints filter alongside minVolumeUsd and you receive only large transfers of that token. This works on the Starter plan ($39/mo), which watches up to 25 tokens at once. Pro and Enterprise watch up to 100 tokens.

Can I get whale alerts for every transfer on Solana?

Yes, on the Pro plan ($99/mo) or Enterprise. Those plans may subscribe to the transfers stream with no wallets or mints filter, so a subscription with only minVolumeUsd delivers every wallet-to-wallet transfer above your threshold. Starter must send a wallets or mints filter.

Why are some large transfers missing from my whale alerts?

Three reasons. A transfer whose token had no price when it was decoded has a null volumeUsd, and a null fails any volume filter. Moves to program accounts such as deposits, payouts and DEX pools are not transfers. And two wallets exchanging tokens in one transaction is a swap, which arrives on the trades stream instead.

Does Anaxer label whale wallets or exchange wallets?

No. Anaxer does not label wallets, so a transfer event carries the sender and recipient addresses but not who owns them. It does not know which address is an exchange or a fund. If your alerts need names, keep your own address labels and join them to the from and to fields.

How far back can I look up a whale wallet's transfers?

GET /v1/wallets/:wallet/transfers returns a wallet's transfers from the last 7 days, newest first. Transfers are stored only from 11 November 2026, the day the feature launched, so there is no earlier history.

Do Solana whale alerts built on transfers include large DEX trades?

No. The transfers stream carries wallet-to-wallet transfers only. Large swaps arrive on the Anaxer trades stream, which also takes minVolumeUsd, so a monitor that needs both subscribes to both channels, which one connection can hold.

Last updated November 11, 2026.

Start streaming Solana data

Anaxer gives you real-time pump.fun and PumpSwap streams and REST APIs on a flat plan. The free tier needs no card.

Written by Anton Gilborn · Senior Platform Engineer, Anaxer

Anton is a senior platform engineer at Anaxer, where he builds the real-time streaming infrastructure behind its Solana data API - token launches, graduations, DEX trades, prices, and transfers. He writes about low-latency streaming architecture and building fast on-chain apps.

Keep reading