◈   ⌘ api · Intermediate

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.

Uncle Solieditor · voc · 06.05.2026 ·views 12
◈   Contents
  1. → What Is the Moralis Streams API?
  2. → Setting Up Your First Moralis Stream
  3. → Receiving and Parsing Blockchain Events
  4. → Building a Whale Alert System With Python
  5. → Connecting Moralis Streams to Your Trading Workflow
  6. → Frequently Asked Questions

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.

What Is the Moralis Streams API?

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.

Setting Up Your First Moralis Stream

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.

Receiving and Parsing Blockchain Events

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.

Building a Whale Alert System With Python

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.

Connecting Moralis Streams to Your Trading Workflow

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.
Moralis Streams vs. manual polling — key trade-offs
DimensionMoralis StreamsPolling a Node
LatencyNear real-time (seconds after block)Depends on interval (often 10-30s lag)
InfrastructureManaged by MoralisYou run and maintain nodes
Cost modelPay per records deliveredPay for node + compute uptime
Missed eventsMoralis retries failed deliveriesYour code must handle gaps manually
Multi-chainSingle API for 20+ chainsSeparate node per chain

Frequently Asked Questions

Is the Moralis Streams API free to use?
Yes, Moralis offers a free tier that covers a limited number of records delivered per month — enough to prototype and test your stream setup. Paid plans scale based on records consumed and unlock higher limits, additional chains, and priority support. Check the Moralis pricing page for current tier details.
How quickly does Moralis deliver events after a block is confirmed?
Delivery typically happens within a few seconds of the transaction being included in a finalized block. The exact latency depends on the chain's block time and network conditions. For time-sensitive trading strategies, factor in your own server processing time and downstream network latency on top of Moralis delivery time.
Can I track unconfirmed mempool transactions with Moralis Streams?
Moralis Streams operates on confirmed transactions by default. For mempool (pending transaction) monitoring, you would need a dedicated mempool subscription service or a direct connection to an Ethereum node with txpool access. Moralis does support some pending transaction visibility depending on the plan tier.
How do I verify that a webhook actually came from Moralis?
Moralis signs each webhook payload using HMAC-SHA3-256 with your stream's secret key and includes the signature in the x-signature header. Compute the HMAC of the raw request body on your server and compare it to the header value — reject any request where they do not match. The SDK provides a Moralis.Streams.verifySignature() helper that handles this for you in JavaScript.
Can I use Moralis Streams to monitor wallets on multiple chains simultaneously?
Yes. When creating a stream you can pass an array of chain IDs, and Moralis will deliver events from all specified chains to the same webhook URL. Each payload includes a chainId field so your handler can route events by chain. This makes cross-chain wallet monitoring straightforward without running separate streams per network.
What happens if my webhook server goes down and misses events?
Moralis retries failed webhook deliveries with exponential backoff for a defined window. If your server is down long enough that retries are exhausted, those events will not be re-delivered automatically. For production systems, use a load balancer with health checks and store raw payloads to a queue or database immediately on receipt so downstream processing failures do not cause data loss.

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.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies