Build a Whale Alert Bot with the OpenScan.AI API

Build a Whale Alert Bot with the OpenScan.AI API

In this tutorial you’ll build a small Node.js bot that watches an address on XDC Network and posts an alert to a webhook (Slack, Discord, or your own service) whenever it receives a large transfer. You’ll use the public REST API at xdcscan.io — no signup required, and the whole thing is under 100 lines.

What you’ll learn

  • Querying an address’s transaction history with /api/v2/addresses/{hash}/transactions
  • Filtering and deduplicating results
  • Posting alerts to an incoming webhook
  • When to switch from polling to the watchlist/webhook service

Prerequisites

  • Node.js 18+ (for built-in fetch)
  • A webhook URL (Slack and Discord both offer incoming webhooks)
  • The address you want to watch

Step 1: Fetch recent transactions

The API returns an address’s transactions newest-first:

const WATCHED = "0x1234567890abcdef1234567890abcdef12345678";
const res = await fetch(
`https://xdcscan.io/api/v2/addresses/${WATCHED}/transactions`,
);
const { items } = await res.json();
console.log(items[0].hash, items[0].value);

Each item includes hash, from, to, value (in wei), timestamp, and status.

Step 2: Filter for large incoming transfers

We only care about successful transactions to the watched address above a threshold:

const THRESHOLD_XDC = 100_000;
function isWhaleTransfer(tx) {
const amount = Number(tx.value) / 1e18;
return (
tx.status === "ok" &&
tx.to?.hash?.toLowerCase() === WATCHED.toLowerCase() &&
amount >= THRESHOLD_XDC
);
}

Step 3: Deduplicate with a seen-set

Polling returns overlapping windows, so track hashes you’ve already alerted on:

const seen = new Set();
async function poll() {
const res = await fetch(
`https://xdcscan.io/api/v2/addresses/${WATCHED}/transactions`,
);
const { items } = await res.json();
for (const tx of items) {
if (seen.has(tx.hash) || !isWhaleTransfer(tx)) continue;
seen.add(tx.hash);
await alert(tx);
}
}

Step 4: Post the alert

const WEBHOOK = process.env.ALERT_WEBHOOK_URL;
async function alert(tx) {
const amount = (Number(tx.value) / 1e18).toLocaleString();
await fetch(WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text:
`🐋 Whale alert: ${amount} XDC received\n` +
`From: ${tx.from.hash}\n` +
`Tx: https://xdcscan.io/tx/${tx.hash}`,
}),
});
}

Step 5: Run it on an interval

setInterval(() => poll().catch(console.error), 15_000);
poll().catch(console.error);

Run with:

Terminal window
ALERT_WEBHOOK_URL="https://hooks.slack.com/services/..." node whale-alert.js

Step 6: Harden it (optional)

  • Seed the seen-set on startup: alert only on transactions newer than the bot’s start time, or pre-mark the first page as seen.
  • Handle 429s: if you poll faster, add an API key and back off on rate-limit responses — see API Keys & Rate Limits.
  • Follow token transfers too: /api/v2/addresses/{hash}/token-transfers?type=ERC-20 catches whale moves in stablecoins, not just native XDC.

Step 7: Go serverless — use watchlist webhooks instead

Polling is fine for a hobby bot, but production alerting shouldn’t run a loop on your laptop. Add the address to a watchlist with webhook notifications and OpenScan.AI pushes each matching transaction to your endpoint as it’s indexed:

Terminal window
curl -X POST https://xdcscan.io/account/api/v1/user/watchlist \
-H "Authorization: Bearer $OPENSCAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"address_hash": "0x1234567890abcdef1234567890abcdef12345678",
"name": "Whale watch",
"notification_settings": { "native": { "incoming": true } },
"notification_methods": { "webhook": true }
}'

Your webhook receiver then just verifies the signature and reposts — see Alerts & Webhooks for the payload format and HMAC verification.

Next steps