Okay, grab your terminal. You're gonna love this. First thing I do when I wanna track some Solana action? Spin up a dead simple WebSocket sub to watch an account balance change. Why? 'Cause it's instant real time feedback. No polling bullshit.
Install @solana/web3.js real quick: npm init -y && npm i @solana/web3.js. Paste this into a file called watch.js:
const solanaWeb3 = require('@solana/web3.js');
const connection = new solanaWeb3.Connection('https://api.devnet.solana.com');
const wsEndpoint = 'wss://api.devnet.solana.com'; const ACCOUNTTOWATCH = new solanaWeb3.PublicKey('YOURWALLETHERE'); const subId = connection.onAccountChange(ACCOUNTTOWATCH, (accountInfo) => { console.log('Balance changed!', accountInfo.lamports / solanaWeb3.LAMPORTSPERSOL, 'SOL');
}, 'confirmed'); console.log('Watching.. Sub ID:', subId);
setTimeout(() => connection.removeAccountChangeListener(subId), 30000); // Clean up after 30s
Run node watch.js. Send some devnet SOL to that address. Boom-logs it instantly. That's your "why it works": Solana pushes changes over WebSocket. Milliseconds. In my experience, free public endpoints flake out after a bit, so snag a QuickNode free tier URL pronto.
Picture this: You're building a bot that snags new token launches. Or alerting on whale dumps. Or just debugging your program's tx fails. Events are your eyes on chain. Transaction logs, account updates-it's all there.
The thing is, Solana blasts ~50k TPS sometimes. Polling RPC every second? You'll hit rate limits and burn cash. WebSockets? Free pushes. RPC polling for history. Webhooks? We'll hit that later-they're like WebSockets but outsourced.
Sound familiar? Yeah, I chased ghosts for hours polling once. Never again.
So WebSockets connect to Solana's wss://api.mainnet beta.solana.com (or your RPC provider). You subscribe once, get events forever. Until it stales-then reconnect, dummy.
wss://api.devnet.solana.com. Better? QuickNode dashboard → copy WS URL."confirmed" commitment. "Finalized" lags 1-2 slots.Issue? Connection drops after 10 mins idle. I usually ping every 30s: connection._rpcWebSocket.send('ping');. Keeps it alive.
Want DeFi swaps? NFT mints? Subscribe to a program ID.
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DAconnection.onLogs( new solanaWeb3.PublicKey('PROGRAMIDHERE'), (logs) => { if (logs.err) return; console.log('Logs:', logs.logs); // Parse for events like "Transfer" }, 'confirmed'
);
Logs are arrays of strings. Anchor programs emit base64 events-decode 'em with borsh. Why bother? Catch "InitializeMint" for new tokens before Dexscreener indexes.
WebSockets are future only. Need past txes? Poll RPC. It's dumb simple but rate limited on free tiers (100 reqs/sec).
I use this for backfills. Like, "gimme last 100 txs on this wallet."
| Method | What It Does | Limit | Cost |
|---|---|---|---|
| getSignaturesForAddress | Tx signatures for addr | 1000 | Low |
| getParsedTransactions | Full tx details | Batch 100 sigs | Medium |
| getAccountInfo | Current balance/state | 1 | Free ish |
Full script? Here's one I tweaked from QuickNode guides. Tracks 5 txs, parses instructions.
const getTxHistory = async (address, numTx = 5) => { const pubkey = new solanaWeb3.PublicKey(address); const connection = new solanaWeb3.Connection('YOURRPCHTTP_URL'); const sigs = await connection.getSignaturesForAddress(pubkey, { limit: numTx }); const sigList = sigs.map(tx => tx.signature); const details = await connection.getParsedTransactions(sigList, { maxSupportedTransactionVersion: 0 }); sigs.forEach((sig, i) => { const date = new Date(sigs[i].blockTime * 1000); console.log(Tx ${i+1}: ${sig.signature} at ${date}); const instructions = details[i]?.transaction.message.instructions; instructions?.forEach((inst, n) => { console.log( Instr ${n+1}: ${inst.programId.toString()}); }); });
}; getTxHistory('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // USDC mint
Run it. Spots program IDs like Pump.fun swaps. Problem? Big wallets have 10k+ txs. Paginate with before: lastSig. Fees? ~1-5ms per call, dirt cheap.
Best setup? WebSocket for live, RPC for catch up. On app start:
In my experience, this catches 99% without gaps. Why does it matter? Slots finalize in ms. Miss one? Your bot buys at bad price.
Hate managing reconnections? Webhooks push to your endpoint. Helius or QuickNode do this sweet.
Setup: Sign up Helius.dev (free tier 1k reqs/day). Create webhook for "account updates" on your wallet. POSTs JSON to your ngrok URL.
Code a tiny Express server:
app.post('/webhook', (req, res) => { const event = req.body; if (event.account?.lamportsChanged) { console.log('Balance ping!', event.account.lamports / 1e9); } res.sendStatus(200);
});
Pros: No WS in your code. Cons: Vendor lock, ~$50/mo for heavy use. I use for prod alerts only.
They retry failed deliveries 5x. Always 200 OK fast. Timeouts kill ya. Test with ngrok.
Okay, real talk. Public RPCs throttle at 100 connos. Get QuickNode Marketplace-$9/mo starter covers 25M units.
Reconnects: Wrap subs in try catch. Exponential backoff: 1s → 2s → 4s.
Parsing logs? Anchor events are Program log: Event: base64data. Use @coral xyz/anchor to unpack.
Scale? Shard subs across processes. One per program. Redis pubsub for internal events.
Let's build something fun. Watch Raydium pools for new tokens.
675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8Full code's like 100 lines. DM if you want it-I've sniped 50x pumps this way. Risks? Rug pulls. Always sim on devnet first.
If JS ain't your vibe, Solana.py rocks. Async WS subs out the box.
import asyncio
from solana.rpc.websocket_api import connect
from solders.pubkey import Pubkey async def main(): async with connect("wss://api.mainnet beta.solana.com") as ws: await ws.accountsubscribe(Pubkey.fromstring("YOUR_ADDR")) async for msg in ws: print(msg) asyncio.run(main())
Types: accountsubscribe, logssubscribe, signature_subscribe. Clean. Runs forever-Ctrl+C to kill.
What's next? Pick one method, code it today. WebSockets for speed. RPC for depth. Webhooks for ease. Hit snags? Common: "Subscription not found"-means bad commitment level. Fix: Stick to "confirmed".
You're set. Go track some events. 🚀
(