Moralis Web3 API Streams: Real-Time Blockchain Data for Traders
Moralis Web3 Streams API enables real-time blockchain event monitoring directly in your app. Track wallet activity, token transfers, and contract events with webhook-based delivery.
Moralis Web3 Streams API enables real-time blockchain event monitoring directly in your app. Track wallet activity, token transfers, and contract events with webhook-based delivery.
If you have ever tried to monitor on-chain activity by polling a blockchain node every few seconds, you already know how painful and unreliable that approach gets at scale. Rate limits hit you, data arrives late, and your infrastructure costs climb fast. Moralis Web3 Streams solves this at the root. Instead of pulling data, you subscribe to blockchain events and Moralis pushes them to your server the moment they happen — wallet movements, token transfers, DEX swaps, contract interactions, everything. For traders building signal systems, whale trackers, or automated bots, this is the infrastructure layer that makes real-time strategies actually viable.
Moralis Streams is a webhook-based event delivery system for EVM-compatible blockchains. You define what you want to watch — a specific wallet address, a smart contract, an event topic — and Moralis handles all the node infrastructure, indexing, and delivery. Your server receives a structured JSON payload every time a matching event fires on-chain. The service supports Ethereum, BNB Chain, Polygon, Avalanche, Arbitrum, Base, and over a dozen other networks, so you can track activity across the entire multi-chain ecosystem from a single API integration. What makes it genuinely useful for trading applications is the filtering system: you can specify ABI definitions to decode specific event types, filter by token value thresholds, and subscribe to multiple addresses in a single stream, which keeps your webhook handler focused on signals that actually matter.
Getting started requires a Moralis account and an API key from the dashboard at moralis.io. Install the SDK with npm, initialize the client with your key, then call Streams.add() with the chain, webhook URL, and event definition you want to track. The example below creates a stream that monitors USDT transfers on Ethereum and posts each matching event to your server. The topic0 field encodes the Keccak-256 hash of the Transfer event signature, which is how Moralis knows which log entries to capture. You can add multiple addresses and multiple topic signatures to a single stream — useful when you want to watch activity across several tokens or wallets simultaneously without multiplying your webhook infrastructure.
const Moralis = require("moralis").default;
const { EvmChain } = require("@moralisweb3/common-evm-utils");
const runApp = async () => {
await Moralis.start({
apiKey: process.env.MORALIS_API_KEY,
});
const stream = await Moralis.Streams.add({
chains: [EvmChain.ETHEREUM],
description: "Monitor large USDT transfers",
tag: "usdt-whale-tracker",
webhookUrl: "https://your-server.com/webhook",
includeNativeTxs: false,
abi: [
{
anonymous: false,
inputs: [
{ indexed: true, name: "from", type: "address" },
{ indexed: true, name: "to", type: "address" },
{ indexed: false, name: "value", type: "uint256" },
],
name: "Transfer",
type: "event",
},
],
topic0: ["Transfer(address,address,uint256)"],
address: "0xdac17f958d2ee523a2206206994597c13d831ec7", // USDT contract
});
console.log("Stream created with ID:", stream.id);
};
runApp().catch(console.error);
Once the stream is created, Moralis returns a stream ID you should store — you will need it later to update filters, add more addresses, or delete the stream entirely. The webhookUrl must be a publicly accessible endpoint. During development, tools like ngrok let you expose a local port and test the full flow without deploying. Moralis will immediately send a test ping to your webhook URL when the stream is created — your server needs to respond with HTTP 200 or the stream will be marked as failing.
Your webhook endpoint receives a JSON body containing everything Moralis decoded from the block: native transactions, ERC-20 token transfers, internal transactions, and raw logs. For token tracking purposes, the erc20Transfers array is the most immediately useful — it gives you pre-decoded transfer data with human-readable fields like tokenSymbol, tokenDecimals, from, to, and value. Before processing any payload, you should verify the x-signature header to confirm the request genuinely came from Moralis and has not been tampered with. Skipping signature verification is a common mistake that leaves your webhook open to spoofed events — a serious problem if your system is triggering trades or alerts based on the incoming data.
const express = require("express");
const Moralis = require("moralis").default;
const app = express();
app.use(express.json());
app.post("/webhook", async (req, res) => {
try {
// Verify the webhook signature before trusting the payload
await Moralis.Streams.verifySignature({
body: req.body,
signature: req.headers["x-signature"],
});
const { erc20Transfers, txs, block } = req.body;
if (!erc20Transfers || erc20Transfers.length === 0) {
return res.sendStatus(200);
}
erc20Transfers.forEach((transfer) => {
const decimals = parseInt(transfer.tokenDecimals, 10);
const amount = Number(transfer.value) / Math.pow(10, decimals);
const symbol = transfer.tokenSymbol;
if (amount >= 100000) {
console.log(
`[Block ${block.number}] Large ${symbol} transfer: ${amount.toLocaleString()} ${symbol}`
);
console.log(` From: ${transfer.from}`);
console.log(` To: ${transfer.to}`);
console.log(` Tx: ${transfer.transactionHash}`);
// Hand off to your alerting/signal system here
}
});
res.sendStatus(200);
} catch (err) {
console.error("Webhook error:", err.message);
res.sendStatus(400);
}
});
app.listen(3000, () => console.log("Listening for Moralis events on :3000"));
The server above filters for transfers above 100,000 units and logs them to the console. In practice you would route these events to your alerting pipeline — Telegram bots, Discord webhooks, database writes, or directly into your order execution logic. The response time matters: Moralis will retry delivery if your server does not respond within a few seconds, so keep the webhook handler fast and move any heavy processing to an async queue.
Python is a natural fit for analytics-heavy trading workflows, and Flask makes it straightforward to handle Moralis webhooks. The example below extends the basic handler with HMAC signature verification and Telegram alert delivery, so large transfers trigger a formatted message directly to your trading channel. This kind of on-chain whale tracker complements what platforms like VoiceOfChain already deliver — VoiceOfChain pushes curated market signals based on aggregated on-chain and off-chain data, while a custom Moralis stream lets you build hyper-specific filters around individual contracts or wallets that matter to your personal strategy. The combination gives you both broad market context and granular on-chain visibility.
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
import requests
import os
app = Flask(__name__)
MORALIS_SECRET = os.environ["MORALIS_WEBHOOK_SECRET"]
TELEGRAM_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
TELEGRAM_CHAT_ID = os.environ["TELEGRAM_CHAT_ID"]
def verify_signature(raw_body: bytes, signature: str) -> bool:
computed = hmac.new(
MORALIS_SECRET.encode("utf-8"),
raw_body,
hashlib.sha3_256,
).hexdigest()
return hmac.compare_digest(computed, signature)
def send_telegram_alert(message: str) -> None:
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
requests.post(url, json={"chat_id": TELEGRAM_CHAT_ID, "text": message, "parse_mode": "HTML"})
@app.route("/webhook", methods=["POST"])
def handle_webhook():
raw_body = request.get_data()
signature = request.headers.get("x-signature", "")
if not verify_signature(raw_body, signature):
return jsonify({"error": "Invalid signature"}), 401
data = request.json
transfers = data.get("erc20Transfers", [])
for t in transfers:
try:
decimals = int(t.get("tokenDecimals", 18))
amount = int(t.get("value", 0)) / (10 ** decimals)
symbol = t.get("tokenSymbol", "UNKNOWN")
if amount >= 500_000: # Alert on transfers >= 500k tokens
from_short = t["from"][:8] + "..." + t["from"][-6:]
to_short = t["to"][:8] + "..." + t["to"][-6:]
msg = (
f"Whale Alert\n"
f"Token: {symbol}\n"
f"Amount: {amount:,.0f} {symbol}\n"
f"From: {from_short}\n"
f"To: {to_short}\n"
f"Tx: https://etherscan.io/tx/{t['transactionHash']}"
)
send_telegram_alert(msg)
except (KeyError, ValueError) as e:
app.logger.error("Parse error: %s", e)
return jsonify({"status": "ok"}), 200
if __name__ == "__main__":
app.run(port=3000)
Store your secrets in environment variables, never in source code. The MORALIS_WEBHOOK_SECRET is available from the Moralis dashboard under the Streams section — it is the same secret used across all your streams, so you only need to configure it once. For production deployments, run this behind nginx with TLS and consider using a task queue like Celery to keep webhook response times under 2 seconds even when Telegram is slow.
The real power of a streaming setup shows when you connect on-chain events to trading actions. A typical pattern is to watch the mempool or finalized blocks for large inflows into exchange deposit addresses — when a whale moves significant USDT or ETH into a Binance hot wallet, it often precedes a sell. Similarly, tracking outflows from Coinbase Custody addresses can indicate institutional accumulation before a move. You can build the same logic for DeFi: monitor Uniswap or Curve liquidity events, watch for large positions opening on Aave or Compound, or track governance votes that precede token price movements.
Traders running bots on Bybit or OKX often pair this kind of on-chain data with exchange order book data to build confluence-based entry signals. For example, if a whale is accumulating ETH on-chain (large outflows from exchange wallets) and simultaneously the order book on Bybit shows bid absorption at a key level, that convergence is a higher-confidence signal than either data point alone. Moralis Streams provides the on-chain half of that equation with minimal infrastructure overhead. Platforms like Gate.io and Bitget also expose websocket market data APIs that you can subscribe to alongside your Moralis webhook, giving you a combined view without polling either source.
Tip: VoiceOfChain aggregates curated on-chain and market signals into ready-made trading alerts. Use it alongside your Moralis stream setup — VoiceOfChain handles broad market signal generation while your custom streams handle hyper-specific wallet and contract monitoring tailored to your strategy.
| Dimension | Moralis Streams | Polling a Node |
|---|---|---|
| Latency | Near real-time (seconds after block) | Depends on interval (often 10-30s lag) |
| Infrastructure | Managed by Moralis | You run and maintain nodes |
| Cost model | Pay per records delivered | Pay for node + compute uptime |
| Missed events | Moralis retries failed deliveries | Your code must handle gaps manually |
| Multi-chain | Single API for 20+ chains | Separate node per chain |
Moralis Streams removes the most painful part of building on-chain trading tools — the raw infrastructure. You stop worrying about node connections, block parsing, and missed events, and start focusing on the signal logic that actually gives you an edge. The pattern is the same whether you are tracking whale wallets feeding into Binance, monitoring DEX liquidity shifts, or building a portfolio alert system across Polygon and Arbitrum. Define what matters, point Moralis at it, and let the webhooks drive your application. The code examples above give you a working foundation — from there, the complexity you add should come from your trading logic, not from the data pipeline underneath it.