Etherscan API Tutorial: Track Whale Wallets in 50 Lines of JavaScript
You don't need Nansen, Arkham, or a paid analytics service to track whale wallet activity. The Etherscan API exposes every Ethereum transaction publicly, with a generous free tier (5 requests/second with a free key, 1 every 5 seconds without). In this tutorial we'll build a real-time whale tracker in 50 lines of vanilla JavaScript that runs entirely in the browser.
Code in this tutorial is live on AlphaDesk — open the demo, hit DevTools → Network tab, and watch real-time Etherscan calls stream in. Nothing is mocked.
A function fetchWhaleTransactions(address) that returns the latest big-money moves ($50k+) for any Ethereum address, with de-duplication and graceful failure. Plug it into your own dashboard, Telegram bot, or alert system.
Step 1 — Pick your wallets
Famous wallets are public. Etherscan labels major addresses (Vitalik, Binance hot wallets, Coinbase, Kraken, market makers like Wintermute and Jump Trading). Here's a starter list of 8 high-activity addresses that move millions daily:
const WHALES = [
{ addr: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', label: 'Vitalik' },
{ addr: '0x28C6c06298d514Db089934071355E5743bf21d60', label: 'Binance 14' },
{ addr: '0xF977814e90dA44bFA03b6295A0616a897441aceC', label: 'Binance 8' },
{ addr: '0x71660c4005BA85c37ccec55d0C4493E66Fe775d3', label: 'Coinbase 1' },
{ addr: '0x503828976D22510aad0201ac7EC88293211D23Da', label: 'Coinbase 2' },
{ addr: '0x2910543Af39abA0Cd09dBb2D50200b3E800A63D2', label: 'Kraken' },
{ addr: '0x0000000000A5c50F35baeD3b2C76b6e78D03d35F', label: 'Wintermute MM' },
{ addr: '0x5754284f345afc66a98fbB0a0Afe71e0F007B949', label: 'Tether Treasury' },
];
You can find more by browsing Etherscan's label cloud. Stick to addresses with high activity — cold storage wallets won't show useful signal.
Step 2 — Fetch latest transactions
Etherscan's txlist endpoint returns transactions for any address. CORS is enabled, so you can call it directly from the browser:
async function fetchTxns(address) {
const url = `https://api.etherscan.io/api`
+ `?module=account&action=txlist`
+ `&address=${address}`
+ `&startblock=0&endblock=99999999`
+ `&page=1&offset=5&sort=desc`;
const res = await fetch(url);
const json = await res.json();
if (json.status !== '1' || !Array.isArray(json.result)) return [];
return json.result;
}
The offset=5&sort=desc tells Etherscan: give us the 5 most recent transactions. Don't request more than you need — the free tier rate-limits you to ~1 req/5s without a key.
Step 3 — Filter for whale-sized transactions
Each transaction has a value field in wei (1 ETH = 10^18 wei). Convert to ETH, multiply by current ETH price, filter for $50k+:
const ETH_PRICE = 3500; // pull from CoinGecko in production
function isWhaleTransaction(tx) {
const ethValue = parseFloat(tx.value) / 1e18;
const usdValue = ethValue * ETH_PRICE;
return usdValue >= 50000;
}
For real production code, fetch ETH price live from CoinGecko's free endpoint:
async function getEthPrice() {
const r = await fetch(
'https://api.coingecko.com/api/v3/simple/price'
+ '?ids=ethereum&vs_currencies=usd'
);
const j = await r.json();
return j.ethereum.usd;
}
Step 4 — De-duplicate seen transactions
If you poll every 30 seconds, you'll see the same recent transactions repeatedly. Use a Set of seen tx hashes to filter:
const seenHashes = new Set();
function processNewTxns(txns) {
return txns.filter(tx => {
if (seenHashes.has(tx.hash)) return false;
seenHashes.add(tx.hash);
return true;
});
}
// Prevent unlimited memory growth
if (seenHashes.size > 500) {
const first = seenHashes.values().next().value;
seenHashes.delete(first);
}
Step 5 — Putting it all together
Here's the complete whale tracker in ~50 lines:
const WHALES = [/* ... see step 1 ... */];
const seenHashes = new Set();
let ethPrice = 3500;
async function refreshEthPrice() {
try {
const r = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd');
const j = await r.json();
if (j.ethereum?.usd) ethPrice = j.ethereum.usd;
} catch (e) { /* keep last price */ }
}
async function checkWhale(whale) {
const url = `https://api.etherscan.io/api?module=account&action=txlist`
+ `&address=${whale.addr}&page=1&offset=5&sort=desc`;
try {
const res = await fetch(url);
const j = await res.json();
if (j.status !== '1') return [];
return j.result.filter(tx => {
if (seenHashes.has(tx.hash)) return false;
const usd = (parseFloat(tx.value)/1e18) * ethPrice;
if (usd < 50000) return false;
seenHashes.add(tx.hash);
return true;
}).map(tx => ({
wallet: whale.label,
direction: tx.from.toLowerCase() === whale.addr.toLowerCase() ? 'sent' : 'received',
ethAmount: parseFloat(tx.value)/1e18,
usdAmount: (parseFloat(tx.value)/1e18) * ethPrice,
txHash: tx.hash,
timestamp: parseInt(tx.timeStamp),
}));
} catch (e) { return []; }
}
async function scanAllWhales() {
await refreshEthPrice();
const results = [];
for (const w of WHALES) {
results.push(...await checkWhale(w));
// Stay under rate limit (1 req/5s without key)
await new Promise(r => setTimeout(r, 6000));
}
return results.sort((a, b) => b.timestamp - a.timestamp);
}
// Run every 5 minutes
setInterval(async () => {
const moves = await scanAllWhales();
moves.forEach(m => {
console.log(`${m.wallet} ${m.direction} ${m.ethAmount.toFixed(2)} ETH ($${(m.usdAmount/1000).toFixed(0)}K)`);
// → DOM, Telegram, Discord, etc.
});
}, 5 * 60 * 1000);
scanAllWhales();
Drop this into a <script> tag, open the page, watch the console.
Going further
1. Get a free Etherscan API key
Sign up at etherscan.io/register. Append &apikey=YOUR_KEY to the URL — you get 5 req/sec instead of 1 req/5s. That's a 25x speedup for free.
2. Token transfers, not just ETH
Use action=tokentx instead of txlist to get ERC-20 transfers. The value field will be in token units; you'll need to look up each token's decimals.
3. Telegram alerts
Once you have the moves, fire them to a Telegram bot:
const TG_TOKEN = 'your-bot-token';
const TG_CHAT_ID = 'your-chat-id';
async function sendTelegram(text) {
await fetch(`https://api.telegram.org/bot${TG_TOKEN}/sendMessage`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ chat_id: TG_CHAT_ID, text, parse_mode: 'HTML' }),
});
}
4. Solana support
Ethereum-only? Layer in Helius (free 100k req/month) for Solana whale tracking. The pattern is identical — fetch txns, filter by USD value, de-dupe.
Production gotchas
- Rate limits: Without a key, 1 req per 5 seconds. With a free key, 5 req/sec. With Pro tier ($199/mo) it's 30 req/sec. For 25 whales scanning every 5 min, free key is plenty.
- CORS: Etherscan supports CORS; you can call directly from browser. If you hide the key behind a backend proxy, set
Cache-Controlheaders to reduce calls. - Cold wallets: Most "famous" addresses don't move daily. Filter your wallet list for active addresses (those with txns in the last 30 days).
- Internal txns:
txlistonly returns external txns. For contract internal transfers (DEX trades), useaction=txlistinternal.
AlphaDesk includes 25 pre-loaded whale wallets, real-time scanning, dedup, and a polished UI — all in vanilla HTML/CSS/JS. Free demo, paid full source.
See it live →FAQ
Why not use The Graph or another indexer?
The Graph is more powerful but requires GraphQL knowledge and protocol-specific subgraphs. For "track these 25 wallets," Etherscan's REST API is faster to build with and free.
How fast can I detect a whale move?
Etherscan indexes blocks within ~12 seconds of confirmation. Free tier (1 req/5s without key, 5 req/sec with key) means realistic detection latency is 30s–2min depending on how many wallets you scan.
Will Etherscan ban me for scraping?
Their free tier ToS is fine for personal and commercial use within rate limits. Don't blow past 5 req/sec; cache aggressively. Their abuse detection is generous.