TUTORIAL · 2026

Etherscan API Tutorial: Track Whale Wallets in 50 Lines of JavaScript

April 2026 · 7 min read · Free, no backend, no API key required

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.

What you'll build

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

Want this packaged into a working dashboard?

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.