Skip to main contentSkip to navigation
Blog>Developer Guides>How to Build a Wash Trading Detector: 5 Proven Signals

How to Build a Wash Trading Detector: 5 Proven Signals

Build a wash trading detector on Solana with Birdeye Data: read volume against wallets, trace churning wallets, and flag fake volume by behavior tag.

How to Build a Wash Trading Detector: 5 Proven Signals

A wash trading detector scores how much of a token’s reported volume is real, so you stop trusting a number that a handful of wallets can inflate on purpose. A token can show 10 million dollars of daily volume and still be hollow, because the same small cluster of wallets keeps passing supply back and forth to make the token look busy and climb a trending list. This guide builds a detector that reads that pattern from five Birdeye Data signals: volume against wallet count, trade level wallet concentration, trade cadence, top trader churn, and behavior tag overlap. Every call runs over REST, because the detector scores a token on demand rather than streaming a feed.

No single signal proves wash trading on its own. Real tokens sometimes trade in tight clusters, and manipulated tokens sometimes look clean on one axis. The detector works by stacking five independent signals into one suspicion score, so a token that trips several of them at once is the one worth flagging.

Direct Answer: wash trading detector in 5 steps

Wash trading detector pipeline showing five Birdeye Data signals from token address to a wash suspicion score.

You can build a working wash trading detector with five Birdeye Data calls, each one an independent signal. Read these five steps as a self contained recipe.

  1. Volume baseline. Call GET /defi/token_overview and compare volume against liquidity, trade count, and unique wallet count. Volume that dwarfs liquidity is the first flag.
  2. Wallet trace. Call GET /defi/v3/token/txs-by-volume to pull individual trades with the wallet behind each, then count how few wallets drive the volume.
  3. Cadence check. Call GET /defi/v3/token/trade-data/single with fine frames to catch the even, mechanical rhythm of a bot.
  4. Churn check. Call GET /defi/v2/tokens/top_traders and flag wallets whose buy volume almost exactly matches their sell volume.
  5. Tag overlap. Call GET /token/v1/holder-profile to see whether bundler and sniper wallets dominate the float.

Each step below expands one signal with the exact parameters, a curl example, and the mistake that skews your result.

Baseline your wash trading detector in 1 call

Start with the cheapest signal, because it filters out the obvious cases before you spend credits on trade level analysis. Genuine volume spreads across many wallets and stays in some proportion to liquidity. Fake volume does not.

Endpoint: GET /defi/token_overview

Chains: all

Plan availability: Standard and above

Docs: token overview

One call returns every number this signal needs: liquidity, v24hUSD for 24 hour volume, trade24h for trade count, uniqueWallet24h for the wallet count, and buy24h and sell24h for the split.

ParameterWhereNotes
addressqueryThe token address. This path uses address.
x-chainheaderDefaults to solana.

This signal is a set of ratios, not a single field. Three patterns raise a flag: volume that dwarfs liquidity, a high trade count against a low unique wallet count, and buy volume that sits almost exactly on top of sell volume. Any one of them alone is weak, so read all three together. As a starting point, volume more than roughly 50 times liquidity, or a few hundred trades that map to only a few dozen wallets, is worth escalating to the trade level checks below.

curl --request GET \
  --url '<https://public-api.birdeye.so/defi/token_overview?address=YOUR_TOKEN>' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-chain: solana'

Watch the casing, because it is mixed. Most fields are camelCase, such as v24hUSD and uniqueWallet24h, but holder and global_fees_paid are snake_case. If you map the response assuming camelCase throughout, those two read as undefined. Copy each field name exactly.

The baseline tells you the token looks suspicious in aggregate. It cannot tell you which wallets are behind the volume. That is the next signal, and it is where the detector gets its teeth.

Trace every trade to its wallet

Ratios raise suspicion, but they cannot name a single wallet. To prove concentration you have to go down to the trades themselves and count how few wallets produce the volume.

Endpoint: GET /defi/v3/token/txs-by-volume

Chains: Solana

Plan availability: Starter and above

Docs: trades by volume

Each item in the response carries the owner wallet behind the trade, a side of buy or sell, the from and to legs with amounts, and a source such as raydium. Group the trades by owner, count the unique owners, and compare that against total volume. When a large share of volume traces back to a handful of wallets, the volume is manufactured.

ParameterWhereNotes
token_addressqueryThis path uses token_address, not address.
volume_typequeryusd or amount. Required.
sort_typequerydesc or asc. Required.
limitqueryMax 500.

The parameter to watch is token_address, because this path breaks the usual /defi/ convention and uses token_address. Send address and the call fails.

curl --request GET \
  --url '<https://public-api.birdeye.so/defi/v3/token/txs-by-volume?token_address=YOUR_TOKEN&volume_type=usd&sort_type=desc&limit=100>' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-chain: solana'

There is one counting trap that quietly ruins this signal. A single swap can appear across more than one instruction, so if you count raw response rows you overcount trades and inflate the very volume you are trying to audit. The fix is to deduplicate on the composite key tx_hash plus ins_index plus inner_ins_index, which is unique per real trade. Page through with has_next until you have the window you need.

Wallet concentration shows you that few wallets move the token. It does not show you the rhythm of how they move it. A human cluster and a bot cluster can look similar on concentration alone, so the next signal reads timing.

Check cadence down to 5 seconds

Wash bots trade on a timer. That regularity is invisible at hourly resolution and obvious at the resolution of seconds. This signal reads the token’s trade rhythm at a fine grain that the overview call cannot reach.

Endpoint: GET /defi/v3/token/trade-data/single

Chains: all chains, fine frames on Solana, Base, BSC, and Ethereum

Plan availability: Starter and above

Docs: trade data single

The response returns per frame counts for trade, buy, sell, volume, and unique_wallet. Request fine frames, and an even spread of trades and volume across every frame, with a flat unique wallet count, is the mechanical signature of a bot.

ParameterWhereNotes
addressqueryThis path uses address, not token_address.
framesqueryUp to 8 frames, down to 5 second resolution.
x-chainheaderDefaults to solana.

The parameter here is address, which trips up anyone who just called txs-by-volume. Both live under /defi/v3/token/, yet txs-by-volume wants token_address and this endpoint wants address. Send the wrong one and the call fails. The response is also snake_case, such as unique_wallet, unlike the camelCase overview fields.

curl --request GET \
  --url '<https://public-api.birdeye.so/defi/v3/token/trade-data/single?address=YOUR_TOKEN&frames=1m,5m>' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-chain: solana'

One caveat belongs in your code comment: the endpoint itself runs on every chain, but the custom fine frames down to 5 seconds only work on Solana, Base, BSC, and Ethereum. On other chains you get the 8 default frames and lose the resolution this signal depends on.

Cadence catches the machines. The next signal catches a subtler pattern: wallets that trade a lot but never actually take a position, which is the fingerprint of churn.

Spot churn in the top 10 traders

A wallet that buys and sells nearly equal volume is not investing. It is churning, cycling the same capital to print volume. This signal ranks the busiest wallets and reads their buy against their sell.

Endpoint: GET /defi/v2/tokens/top_traders

Chains: all

Plan availability: Standard and above

Docs: top traders

The response ranks wallets and splits each one’s volume into buy and sell. When volumeBuyUSD sits almost exactly on volumeSellUSD for a top wallet, that wallet is churning. Each item also carries a tags array, so a top wallet tagged bundler or sniper doubles as a signal for the last check.

ParameterWhereNotes
addressqueryThis /defi/v2/ path uses address.
time_framequery30m through 90d, default 24h.
sort_byqueryvolume, trade, total_pnl, unrealized_pnl, realized_pnl, or volume_usd.
limitqueryMax 10.

Sort by volume to surface the wallets doing the most trading, then read the buy and sell split on each. A genuine investor shows a lopsided split, because they entered or exited a position. A wallet within a few percent of even is the one to flag. Watch the casing, because the total is volumeUsd while the splits are volumeBuyUSD and volumeSellUSD, and the suffix changes between sibling fields.

curl --request GET \
  --url '<https://public-api.birdeye.so/defi/v2/tokens/top_traders?address=YOUR_TOKEN&time_frame=24h&sort_by=volume&sort_type=desc&limit=10>' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-chain: solana'

Churn tells you which wallets fake activity. The last signal tells you what kind of wallets they are, by pulling the behavior tags for the whole float.

Cross check behavior tags on the float

Whoever runs a wash operation usually holds the token too, and those wallets tend to already carry a behavior tag from how they acquired it. Reading that overlap turns four trading signals into a picture of who is behind them.

Endpoint: GET /token/v1/holder-profile

Chains: Solana

Plan availability: all plans

Docs: holder profile

The response breaks holders into tags, bundler, sniper, insider, dev, and smart trader, each with holder_count, percent_of_supply, and buy and sell volume. When bundler or sniper wallets hold a large share of supply on a token that already tripped the earlier signals, the case for wash trading gets much stronger.

ParameterWhereNotes
token_addressqueryThis path uses token_address.
include_zero_balancequeryDefaults to true, which counts wallets that traded but no longer hold.
x-chainheaderSolana only for this endpoint.

The parameter is token_address again, matching txs-by-volume and not trade-data/single. Keep the two straight per call. Leave include_zero_balance at its default for a wash check, since wallets that churned volume and exited are exactly the ones you want counted.

curl --request GET \
  --url '<https://public-api.birdeye.so/token/v1/holder-profile?token_address=YOUR_TOKEN>' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-chain: solana'

One timing note: the bundler tag is accurate for tokens created from 1 March 2026 onward, because older tokens are still backfilling. On a legacy token, weight this signal a little lighter and lean on the trade level checks instead.

With all five signals in hand, you have everything the detector needs to score a token.

Track credit usage

Scoring one token spends five calls, and screening a long watchlist adds up fast. Call GET /utils/v1/credits to read your remaining allowance, and render it while you develop, so a runaway screen shows up as a falling number rather than a surprise suspension.

curl --request GET \
  --url '<https://public-api.birdeye.so/utils/v1/credits>' \
  --header 'x-api-key: YOUR_API_KEY'

Rule out the innocent explanations

Before you act on a high score, rule out the legitimate patterns that trip the same signals. Skipping this step is how a detector earns a reputation for crying wolf.

Market makers produce exactly the profile the churn signal looks for. A market maker quotes both sides continuously, so its buy volume tracks its sell volume closely and its wallet shows constant activity without accumulating a position. The tell is consistency across time rather than a burst: a market maker runs the same way all day, while a wash operation usually clusters around a listing, a marketing push, or a chart it wants to look busy.

The cadence signal has a similar blind spot around arbitrage bots. They fire on a schedule because they poll for price gaps on a schedule, and the regular timing that follows has nothing to do with faking volume. Look at the source field: arbitrage spans two pools by definition, while wash trading usually cycles inside one.

Then there are tokens minted an hour ago, which set off almost every signal at once. Few holders, thin liquidity, a high trade to wallet ratio, and a float dominated by whoever bought first: none of that is evidence of manipulation, it is simply what a new token looks like. Weight the signals lighter inside the first day of trading, or pull fresh tokens out of the score entirely and screen them under a separate rule.

Low float distorts the concentration check in the same direction. When only a small share of supply actually circulates, a handful of wallets will naturally account for most of the trading, so measure concentration against circulating supply rather than total supply wherever the data allows.

All four cases share a shape: read one signal alone and an innocent explanation fits it comfortably. Stacking the signals is what removes that ambiguity, and it is the reason the combined score carries more weight than any single flag.

Turn 5 signals into one wash score

No signal is a verdict, so the detector’s real job is to combine them. Assign each signal a threshold and a point, then sum the points into a wash trading score you can rank a whole watchlist by.

A practical starting scheme gives one point per flag: 24 hour volume running more than roughly 50 times liquidity, a trade count that maps to only a small fraction as many unique wallets, buy and sell volume sitting within a few percent of each other, a single owner driving more than a fifth of the traced volume after deduplication, and bundler or sniper wallets holding a large share of the float. A token at four or five points earns a hard look. A token at one point is usually noise.

The thresholds are the part you tune. Calibrate them against two reference sets: tokens you already trust, and tokens you know were manipulated. Slide each cutoff until the clean set scores low and the manipulated set scores high, because the right numbers depend on the market and the chain you screen. Weight the signals if you want, giving the trade level concentration check more pull than the aggregate baseline, since concentration is harder to fake than a single volume number.

Log the per signal breakdown next to the total, not just the score. When you later act on a flag, the breakdown tells you which pattern tripped it, and that record is what lets you sharpen the thresholds over time.

Putting it together: reference architecture

The five signals share one input, the token address, and run independently, so you can fire them in parallel and combine the results into a single suspicion score. No signal is a verdict on its own. The score rises with each flag a token trips.

Vertical reference architecture for a wash trading detector built on five Birdeye Data signals.

Before you ship, walk this checklist:

  • Baseline reads v24hUSD and uniqueWallet24h as camelCase and compares volume against liquidity.
  • Wallet trace calls txs-by-volume with token_address and deduplicates on tx_hash plus ins_index plus inner_ins_index.
  • Cadence calls trade-data/single with address, not token_address, and only trusts 5 second frames on Solana, Base, BSC, and Ethereum.
  • Churn reads volumeBuyUSD against volumeSellUSD and maps them apart from volumeUsd.
  • Tag overlap calls holder-profile with token_address and weights the bundler tag lighter on tokens created before 1 March 2026.
  • The final score treats no single signal as proof, only the stack.

For background on how automated trading concentrates activity into a few accounts on Solana, the Solana documentation on programs and accounts explains the account model these wallets operate through.

FAQ

What is a wash trading detector?

It is a tool that scores how much of a token’s reported volume is likely fake, by stacking several independent signals rather than trusting the headline volume number. This guide builds one on Solana with five Birdeye Data endpoints.

Can one signal confirm wash trading?

No. Every signal here has innocent explanations on its own. The wash trading detector works by combining volume ratios, wallet concentration, cadence, churn, and tag overlap, so a token that trips several at once is the one to flag.

Why does the wash trading detector run on Solana?

Two of the endpoints, trades by volume and holder profile, return data for Solana only, and the fine cadence frames are limited to four chains. The volume baseline and top trader signals work across chains, so you can extend part of the detector, but the core trade level checks stay Solana bound.

How do you avoid overcounting trades?

A single swap can appear across more than one instruction row. Deduplicate on the composite key tx_hash plus ins_index plus inner_ins_index, which is unique per real trade, before you count wallets or volume.

Do you need a paid plan?

All five endpoints are available across the Birdeye Data packages, so the constraint is volume rather than access. Scoring one token costs five calls, so pick a package based on how many tokens you plan to screen rather than which signals you want.

How often should you rescore a token?

Wash patterns shift, so a token that scores clean today can be manipulated next week. For a live watchlist, rescore on the cadence your credit budget allows, and always rescore right before you act on a token’s volume rather than trusting a stale result.

Ready to build your own wash trading detector? Grab an API key and compare plans at Birdeye Data, and browse the full endpoint reference at docs.birdeye.so/reference.

Read next

Build a token investigation dashboard on Solana with Birdeye Data: market stats, holder tags, top traders, fee flow, and supply integrity in one view.
Build a token price alert monitor with Birdeye Data: poll a watchlist, fire absolute and percent alerts, and confirm each move on a closed candle.
Build a whale tracker on Solana with Birdeye Data. Size your threshold, detect large trades, split buy and sell pressure, and stream alerts.
Don't miss out on what's next
Subscribe now and be the first to catch trends, tools, and exclusive updates.
© 2025, Wings Lab Pte. Ltd