Crypto whale alerts and whale tracker for on-chain transfers
Every large on-chain transfer on Ethereum, BNB Chain, Base and HyperEVM, with the wallets and exchanges on both sides labelled: on the WebSocket, as an e-mail alert with no code, or in plain words to your AI. And the whale wallets of each chain, ranked by volume, net flow or profit.
“Show the USDT transfers of 1,000,000 or more on Ethereum in the last hour, and who sent them.”
stream_events“E-mail me every USDT transfer over $1,000,000 on Ethereum.”
alerts_createEvery big transfer as it lands
Subscribe once with a size in dollars; each transfer comes priced, both sides labelled.
Every transfer of $1,000,000 or more, printed as it lands:
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/onchain",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "whales", "channel": "chain.transfers",
"filter": {"min_usd": 1000000}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
s, d, side = e["subject"], e["data"], e["labels"]
print(e["chain"], d["amount"], s["token_symbol"], d["value_usd"],
side.get("from", {}).get("entity", s["from"]), "->", side.get("to", {}).get("entity", s["to"]))
vike-data stream onchain --channel chain.transfers --filter min_usd=1000000 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "whales",
"channel": "chain.transfers",
"filter": {
"min_usd": 1000000
}
}
- Each event carries the token, the amount in tokens, its dollar value (
value_usd) and both wallets with their labels. Size a subscription in dollars withmin_usd, or in tokens withtokenandmin_amount; set both and both must hold. - A transfer is sent once it is priced: about 3 minutes after its block on average (half a minute to 6), not the second it is mined. A token with no price comes with no dollar value (
value_usdnull) and never matchesmin_usd. - The channel runs while someone is subscribed and for 15 minutes after. Subscribe to a quiet channel and the first events come once the next batch is priced.
walletmatches either side (from or to). A list is OR inside a key, keys are AND across, an empty filter matches everything.min_amountneedstoken.- The server pings every 15 s; answer with
pong. 3 missed pongs close the connection. - 1 connection per key on this path, 3 per account, 100 subscriptions on one connection; a newer connection wins and the older one is closed with a resume cursor.
- Delivery is at least once: dedupe on event.event_id. Dropped? Subscribe again with your last
cursorand get what you missed, up to 3 days back. Without a socket, thestream_eventstool reads the same events; the REST catch-up addressstream_inforeports is/v1/onchain/stream-events.
Only the transfers you care about
A size in dollars or in tokens, a chain, a wallet, an exchange, or no contracts.
| What you want | filter | The same from the CLI |
|---|---|---|
| Every transfer of $1,000,000 or more | {"min_usd": 1000000} | vike-data stream onchain --channel chain.transfers --filter min_usd=1000000 |
| 1,000,000 USDT or more on Ethereum | {"token": ["0xdac17f958d2ee523a2206206994597c13d831ec7"], "chain": ["eth"], "min_amount": 1000000} | vike-data stream onchain --channel chain.transfers --filter token=0xdac17f958d2ee523a2206206994597c13d831ec7 --filter chain=eth --filter min_amount=1000000 |
| $1,000,000 or more into or out of an exchange | {"min_usd": 1000000, "wallet_type": ["cex"]} | vike-data stream onchain --channel chain.transfers --filter min_usd=1000000 --filter wallet_type=cex |
| Everything Binance sends or receives | {"entity": ["binance"]} | vike-data stream onchain --channel chain.transfers --filter entity=binance |
| $1,000,000 or more between wallets, no contract on either side | {"min_usd": 1000000, "contract": false} | vike-data stream onchain --channel chain.transfers --filter min_usd=1000000 --filter contract=false |
| Everything one wallet sends or receives (vitalik.eth) | {"wallet": ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"]} | vike-data stream onchain --channel chain.transfers --filter wallet=0xd8da6bf26964af9d7eed9e03e53415d37aa96045 |
chain.transfers: every field and every filter
What an event carries, the filters it takes, and the subscribe call.
chain.transfers
Every token and coin transfer on Ethereum, BNB Chain, Base and HyperEVM, with both wallets and their labels, the amount in tokens, its dollar value and the transaction.
- Path
- wss://stream.vike.io/v1/stream/onchain
- Filters
- wallet, token, chain, min_usd, min_amount, entity, wallet_type, contract
- Arrives
- about 3 minutes after the block
Real transfers captured 2026-11-03T14:01Z, filter {"min_usd": 1000000}
| time (UTC) | chain | amount | USD | from | to |
|---|---|---|---|---|---|
| 2026-11-03 13:55 | base | 532.36 WETH | $1,216,822 | 0x28df…7ade | 0xbb55…6bac |
| 2026-11-03 13:55 | base | 36.26 CBBTC | $2,806,594 | 0x28df…7ade | 0xbb55…6bac |
| 2026-11-03 13:55 | base | 532.36 WETH | $1,216,822 | 0xbb55…6bac | 0x28df…7ade |
| 2026-11-03 13:55 | base | 36.26 CBBTC | $2,806,594 | 0xbb55…6bac | 0x28df…7ade |
| 2026-11-03 13:55 | base | 24.10 CBBTC | $1,865,271 | 0x28df…7ade | 0xbb55…6bac |
| 2026-11-03 13:55 | base | 24.10 CBBTC | $1,865,271 | 0xbb55…6bac | 0x28df…7ade |
Fields
| Field | Type | Description |
|---|---|---|
subject.from | string | Sender |
subject.to | string | Receiver |
subject.token_address | string | The token's contract address |
subject.token_symbol | string | The token's symbol; null when the token is not mapped |
subject.tx_hash | string | The transaction |
data.value | string | The exact raw amount, as text |
data.decimals | integer | The token's decimals; null when unknown |
data.amount | number | Whole tokens; null when the decimals are unknown |
data.value_usd | number | Dollar value; null when the token has no price |
data.priced | boolean | true when it carries a dollar value |
data.block_number | integer | Block |
data.log_index | integer | Log index in the transaction |
data.transfer_type | string | The transfer's type: the chain's coin or a token |
labels.from.contract | boolean | Sender: true when it is a known contract |
labels.to.contract | boolean | Receiver: true when it is a known contract |
A real event
{
"schema": "vike.event.v1",
"event_id": "chain.transfers:base:0x7641408c4fcde980e25f617caa8d48d7b01400194d33b36af89334fd464401b5:920",
"source": "chain.transfers",
"kind": "transfer",
"ts": "2026-11-03T13:55:05.000Z",
"cursor": "000053462379-000920",
"chain": "base",
"subject": {
"from": "0x28df7cdfdaf24b7d8cb7d34933d55b6dec7a7ade",
"to": "0xbb5507daae822ba89c4da61bbfdf4b49a1666bac",
"token_address": "0x4200000000000000000000000000000000000006",
"token_symbol": "WETH",
"tx_hash": "0x7641408c4fcde980e25f617caa8d48d7b01400194d33b36af89334fd464401b5"
},
"data": {
"value": "532363073083930746033",
"decimals": 18,
"amount": 532.3630730839308,
"value_usd": 1216822.275956,
"priced": true,
"block_number": 53462379,
"log_index": 920,
"transfer_type": "erc20"
},
"labels": {
"from": {
"contract": true
}
}
}
Filters
| Field | Type | Description |
|---|---|---|
wallet | list ≤ 1,000 | The wallet on either side: sender or receiver. |
token | list ≤ 200 | The token's contract address. |
chain | list ≤ 50 | The chain the transfer is on. One of: base, bsc, eth, hyperevm. |
min_usd | number | The transfer's dollar value is at least this. A transfer with no dollar value never matches it. |
min_amount | number | The amount in whole tokens is at least this. Needs token: an amount across tokens means nothing. |
entity | list ≤ 50 | The entity behind either side, as its label names it, e.g. binance. |
wallet_type | list ≤ 50 | The wallet type of either side, e.g. cex for an exchange. |
contract | boolean | true: either side is a known contract; false: neither is. |
Subscribe 1,000,000 USDT or more on Ethereum
import json, os
from websockets.sync.client import connect
with connect("wss://stream.vike.io/v1/stream/onchain",
additional_headers={"X-API-KEY": os.environ["VIKE_API_KEY"]}) as ws:
ws.send(json.dumps({"action": "subscribe", "id": "whales", "channel": "chain.transfers",
"filter": {"token": ["0xdac17f958d2ee523a2206206994597c13d831ec7"], "chain": ["eth"], "min_amount": 1000000}}))
for message in ws:
frame = json.loads(message)
if frame["type"] == "ping":
ws.send(json.dumps({"action": "pong"}))
elif frame["type"] == "event":
e = frame["event"]
s, d, side = e["subject"], e["data"], e["labels"]
print(e["chain"], d["amount"], s["token_symbol"], d["value_usd"],
side.get("from", {}).get("entity", s["from"]), "->", side.get("to", {}).get("entity", s["to"]))
vike-data stream onchain --channel chain.transfers --filter token=0xdac17f958d2ee523a2206206994597c13d831ec7 --filter chain=eth --filter min_amount=1000000 # prints NDJSON, reconnects by itself
{
"action": "subscribe",
"id": "whales",
"channel": "chain.transfers",
"filter": {
"token": [
"0xdac17f958d2ee523a2206206994597c13d831ec7"
],
"chain": [
"eth"
],
"min_amount": 1000000
}
}
An e-mail when a whale moves
Pick a token and a size in dollars or in tokens, or one wallet. Nothing to install.
Big transfers of a token
USDT over $1,000,000 on Ethereum is filled in: change the token, the chain or the size on the form. The size can be in dollars or in tokens (1,000,000 USDT); set both and both must hold. One chain per alert. It e-mails the newest match at most once per cooldown, up to 10 a day.
Watch a whale wallet
Paste a wallet: every transfer of $100,000 or more it sends or receives is e-mailed, each one.
With code, or from your AI
vike-data alerts create --label 'USDT over $1M on Ethereum' --token USDT --chain eth --min-usd 1000000 --channel email
# any MCP client; or from the terminal:
vike-data call alerts_create --args '{"label": "USDT over $1M on Ethereum", "alert_type": "transfer", "token_symbol": "USDT", "chain": "eth", "min_value_usd": 1000000, "channels": ["email"]}'
# or a size in tokens: 1,000,000 USDT or more
vike-data call alerts_create --args '{"label": "1M USDT or more on Ethereum", "alert_type": "transfer", "token_symbol": "USDT", "chain": "eth", "min_amount": 1000000, "channels": ["email"]}'
The biggest wallets of a chain
Ranked by volume, net flow or realized profit, over the window you pick.
Wallet discovery
A leaderboard of active wallets on one chain, ranked by volume, inflow, outflow, net flow, transaction count or realized PnL over 24h, 7d, 30d, 90d or all time (on Base and HyperEVM the rolling windows only while their daily refresh is under 2 days old); round-trip hubs are hidden by default.
- Routes
/v1/onchain/wallets/discover- Built on
- Token transfers
- MCP
wallet_discover
GET /v1/onchain/wallets/discover · The biggest wallets of a chain
- CLI
vike-data wallet discover- MCP
wallet_discover· 2 credits- Cost
- 1 request of the hourly limit
Leaderboard of active wallets. Every USD value is in plain dollars. realized_pnl and tokens_profitable are lifetime figures in every window (realized_pnl_basis: "lifetime"), so sort=pnl ranks by lifetime PnL whatever window is.
Parameters
| Parameter | In | Description |
|---|---|---|
window | query | 24h | 7d | 30d | 90d | all. Default 30d; base and hyperevm default to all and serve the other windows only while those windows were refreshed in the last 2 days, otherwise a 400 |
sort | query | volume | inflow | outflow | net_flow | tx_count | pnl. Default volume |
order | query | asc | desc. Default desc |
chain | query | ethereum | bsc | base | hyperevm. Default ethereum |
size | query | Results per page. Default 10, max 20 — one of the two routes in this namespace whose accepted and applied caps agree, alongside /tokens/search |
include_hubs | query | Boolean, default false. Also rank round-trip hubs (flash-loan, MEV and router pass-through wallets: volume of $10B or more, with net flow and realized PnL each within 0.1% of it), which are hidden by default |
Call it
curl -H "X-API-KEY: $VIKE_API_KEY" \
"https://data.vike.io/v1/onchain/wallets/discover?window=7d&sort=net_flow&size=5"
import os, requests
r = requests.get(
"https://data.vike.io/v1/onchain/wallets/discover?window=7d&sort=net_flow&size=5",
headers={"X-API-KEY": os.environ["VIKE_API_KEY"]},
)
print(r.json())
vike-data wallet discover --window 7d --sort net_flow --size 5
# any MCP client; or from the terminal:
vike-data call wallet_discover --args '{"window": "7d", "sort": "net_flow", "size": 5}'
1 study
With the sentence to ask your AI instead. Every block was run against the live API on 2026-11-03.
Top wallets on a chain by net flow, volume or realized PnL
Call /wallets/discover with chain, window and sort; it returns the top 20 wallets at most by volume, inflow, outflow, net flow, transaction count or realized PnL.
- Uses
/wallets/discover- Verified
- live API, 2026-11-03: 4 calls, all 200
The code
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the largest net buyers on Ethereum over the last 7 days
r = requests.get(
"https://data.vike.io/v1/onchain/wallets/discover?window=7d&sort=net_flow&size=5",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the largest net sellers on Ethereum over 7 days
r = requests.get(
"https://data.vike.io/v1/onchain/wallets/discover?window=7d&sort=net_flow&order=asc&size=20",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# the busiest wallets on BSC over 30 days
r = requests.get(
"https://data.vike.io/v1/onchain/wallets/discover?chain=bsc&window=30d&sort=volume&size=20",
headers={"X-API-Key": API_KEY},
)
import os
import requests
API_KEY = os.environ["VIKE_API_KEY"] # your key, read from the environment
# wallets ordered by stored realized PnL, all history
r = requests.get(
"https://data.vike.io/v1/onchain/wallets/discover?window=all&sort=pnl&size=20",
headers={"X-API-Key": API_KEY},
)