Agent Cookbook12 copy-ready prompts for AI coding agents.12 prompts for AI agentsSee recipes
All Blog Posts
Hyperliquid Mempool Stream over gRPC: See Orders 357 ms Before the Public API

Hyperliquid Mempool Stream over gRPC: See Orders 357 ms Before the Public API

By Ben Chatwin 12min read

Hyperliquid's public API tells you about an order after a block includes it. At that point the order is already on the book, filled or rejected. Everyone who watches the public feed sees it at the same moment. The Hyperliquid mempool shows the same order earlier, while it is still a signed request that travels between nodes.

We measured the gap from AWS Tokyo (ap-northeast-1). For 427,020 orders from the 10 most active Hyperliquid traders, Dwellir's gRPC mempool stream (StreamMempoolTxs) delivered 95.7% of them before Hyperliquid's public WebSocket did. The median lead was 357 ms.

The Short Answer

Metric (29 Oct 2026, 15 minutes, AWS Tokyo)Result
Orders matched on client order id427,020
Seen on the mempool stream first95.7%
Median lead over the public orderUpdates WebSocket357 ms
Middle 50% of leads (p25 to p75)292 to 449 ms
90% of orders arrived at least199 ms early
Public orders with a client order id that also appeared in the mempool99.4%
Mempool records per secondabout 29 (one client block each)
Signed actions per secondabout 22,000

Hyperliquid produces blocks in less than a second, so a third of a second is a large share of one block interval. Hyperliquid's order book rules let a trader act on that head start. The next sections show how.

What Is in the Hyperliquid Mempool?

Users sign actions and send them to a node. Nodes gossip those actions to each other in batches called client blocks, and the block proposer picks them up for consensus. A non-validating node that peers with the network receives these client blocks before any block includes them.

Dwellir's Hyperliquid gRPC API exposes that feed as StreamMempoolTxs. Each record is one client block, stamped with the time the gateway received it:

JSON
["2026-10-29T11:50:38.307729883", {
  "tx_hash": "0x63ce9aab0a5bc58a...",
  "signed_actions": [{
    "signature": {"r": "0xad66...", "s": "0x5780...", "v": 27},
    "action": {"type": "order", "grouping": "na", "orders": [
      {"a": 0, "b": true, "p": "81459", "s": "12.47108", "r": false,
       "t": {"limit": {"tif": "Alo"}}, "c": "0x7849acc2c6c2f6f0fe4bc80ef13d1504"}
    ]},
    "nonce": 1793274638200
  }]
}]

Every action arrives exactly as the user signed it. a is the asset id (0 is BTC perp) and b is the side. p and s are price and size, t is the order type and c is the optional client order id. The feed also includes cancels, modifies, TWAP orders, leverage changes, transfers and validator votes.

In our 15-minute window the stream carried, per second:

Action typePer second
cancelByCloid10,134
order9,290
cancel2,146
batchModify599
modify189

Most of this is market makers quoting and pulling quotes. The rest (TWAPs, transfers, leverage changes, vault actions) is a few hundred actions a second.

Why Is a 357 ms Lead an Edge on Hyperliquid?

Hyperliquid does not process a block's actions in arrival order. Within each consensus batch, the block runs:

  1. Actions that send no GTC or IOC order to a book. Post-only (Alo) orders are in this group.
  2. Cancels.
  3. Actions that send at least one GTC or IOC order.

Hyperliquid in-block execution order: actions without GTC or IOC orders run first, then cancels, then GTC and IOC orders, so a cancel in the same block runs before an IOC

A cancel in the same block as an incoming IOC order therefore runs before the IOC. A market maker can see a large aggressive order in the mempool and send a cancel. If the cancel reaches the same block, it runs first. With the public API, the maker learns about the IOC only after it fills against their quote.

This is the main edge. It helps market makers who must protect their quotes from toxic flow. The mempool also helps in these ways:

  • See liquidity before it rests. Our sample included a $1.8 million post-only BTC bid. At the median lead, a bid like that appears in the mempool 357 ms before the public orderUpdates channel reports it. You can adjust your own quotes before the book changes.
  • Read intent as well as results. Leverage changes, margin transfers, TWAP orders and large cancels show what a trader plans to do. The public API shows only what the exchange did with those requests.
  • Measure rejections and inclusion. The exchange rejects some post-only orders because they would cross the book. Match mempool orders to their outcomes to see how often that happens for a strategy, a market or a wallet.

The edge has limits. A pending order can fail, get cancelled or never reach a block. A strategy must treat mempool data as a signal and confirm outcomes from committed block data. The 4.3% of orders that reached the public API first show that gossip does not cover every path into a block.

How Did We Measure the Mempool Lead?

We ran everything on one AWS ap-northeast-1 VM. Both sides used the same clock, so the result has no clock-sync error.

  1. We watched Hyperliquid's public trades channel on wss://api.hyperliquid.xyz/ws for 60 seconds and took the 10 most active addresses.
  2. We subscribed to the public orderUpdates channel for those 10 addresses.
  3. At the same time we streamed StreamMempoolTxs from api-hyperliquid-mainnet-grpc.n.dwellir.com, which resolves to Dwellir's Tokyo edge from Tokyo.
  4. For each order, we matched the client order id (c in the mempool, cloid in orderUpdates). The lead is the public receive time minus the mempool receive time. We skipped the first 5 seconds after subscribing, when the public channel sends its backlog.

The run lasted 15 minutes on 29 October 2026, from about 15:04 UTC.

Hyperliquid mempool lead distribution: 95.7% of 427,020 orders appeared on Dwellir's gRPC mempool stream first, 36.8% by 300 to 400 ms, median lead 357 ms

The same distribution as a table:

Lead (public time minus mempool time)Share of orders
Public first by more than 100 ms2.6%
Public first by up to 100 ms1.7%
Mempool first by 0 to 200 ms5.8%
Mempool first by 200 to 300 ms17.8%
Mempool first by 300 to 400 ms36.8%
Mempool first by 400 to 500 ms16.4%
Mempool first by 500 ms to 1 s17.0%
Mempool first by more than 1 s1.8%

Three things to know when reading these numbers:

  • They understate the lead. Our Python client parsed each multi-megabyte record inside the receive loop, which added about 25 ms of queueing. The compiled hyperliquid-l1-eval client measured a median of 4 ms from the Dwellir gateway to the VM.
  • We matched orders, not cancels. Matching cancels on order id gave a median lead of 266 ms. That match is noisier. A public canceled event can come from a modify or a cancel by client order id, and the order-id match misses those. We leave cancels out of the headline.
  • Some client order ids get reused. About 0.6% of matches had leads above 10 seconds. Those come from reused ids rather than early delivery, and they do not move the median.

How Do You Stream the Hyperliquid Mempool in Python?

The script below follows the BTC perp order flow and prints every order of $250,000 or more as it reaches the mempool. You need Python 3.10 or later, a Dwellir API key with the Hyperliquid gRPC add-on, and a host in or near Tokyo.

BASH
python3 -m venv .venv && source .venv/bin/activate
pip install grpcio grpcio-tools
curl -fsSLo v3.proto https://www.dwellir.com/docs/hyperliquid/grpc/v3.proto
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. v3.proto
export DWELLIR_API_KEY=your-api-key

The reader runs on its own thread and only queues raw records. The main thread does the JSON parsing. If you parse inside the receive loop, the client falls behind a high-volume stream.

PYTHON
import json, os, queue, threading, time, urllib.request

import grpc
import v3_pb2 as pb
import v3_pb2_grpc as rpc

ENDPOINT = "api-hyperliquid-mainnet-grpc.n.dwellir.com:443"
API_KEY = os.environ["DWELLIR_API_KEY"]
COIN, MIN_USD = "BTC", 250_000
RETRY = {grpc.StatusCode.UNAVAILABLE, grpc.StatusCode.DEADLINE_EXCEEDED,
         grpc.StatusCode.DATA_LOSS, grpc.StatusCode.RESOURCE_EXHAUSTED}


def asset_id(coin):
    """Perp asset id = index in the public meta universe (BTC is 0)."""
    req = urllib.request.Request("https://api.hyperliquid.xyz/info", data=b'{"type":"meta"}',
                                 headers={"Content-Type": "application/json"})
    universe = json.load(urllib.request.urlopen(req))["universe"]
    return next(i for i, a in enumerate(universe) if a["name"] == coin)


def reader(records):
    """Drain the stream on its own thread; reconnect from the last receive time."""
    channel = grpc.secure_channel(ENDPOINT, grpc.ssl_channel_credentials(),
                                  options=[("grpc.max_receive_message_length", 256 << 20)])
    gateway = rpc.HyperliquidL1GatewayStub(channel)
    last_ns, backoff = None, 1
    while True:
        try:
            # Rewind 1 s on reconnect: each Tokyo gateway stamps its own receive time.
            pos = pb.Position(timestamp_ns=last_ns - 1_000_000_000) if last_ns else None
            for rec in gateway.StreamMempoolTxs(pb.StreamRequest(position=pos),
                                                metadata=[("x-api-key", API_KEY)]):
                last_ns, backoff = rec.timestamp_ns, 1
                records.put((time.time_ns(), rec.timestamp_ns, rec.data))
        except grpc.RpcError as err:
            if err.code() not in RETRY:
                raise
            time.sleep(backoff)
            backoff = min(backoff * 2, 30)


def main():
    asset = asset_id(COIN)
    records, seen = queue.Queue(maxsize=10_000), set()
    threading.Thread(target=reader, args=(records,), daemon=True).start()
    while True:
        local_ns, node_ns, data = records.get()
        _, block = json.loads(data)
        for signed in block["signed_actions"]:
            action = signed["action"]
            if action.get("type") != "order":
                continue
            for o in action["orders"]:
                if o["a"] != asset:
                    continue
                usd = float(o["p"]) * float(o["s"])
                key = o.get("c") or (o["p"], o["s"], signed["nonce"])
                if usd < MIN_USD or key in seen:
                    continue  # small, or a duplicate after a reconnect
                seen.add(key)
                side = "BUY " if o["b"] else "SELL"
                tif = next(iter(o["t"].get("limit", {}).values()), "trigger")
                print(f"{side} {o['s']:>9} {COIN} @ {o['p']:>8}  ${usd:>12,.0f}  {tif:<4}"
                      f"  node->you {(local_ns - node_ns) / 1e6:5.1f} ms")


if __name__ == "__main__":
    main()

From our Tokyo VM, the first seconds of output looked like this:

TEXT
BUY   12.47108 BTC @    81459  $   1,015,882  Alo   node->you  30.5 ms
SELL    8.5522 BTC @    81546  $     697,398  Alo   node->you  30.5 ms
SELL  15.48308 BTC @    81534  $   1,262,397  Gtc   node->you  32.6 ms
BUY   21.90812 BTC @    81493  $   1,785,358  Alo   node->you  34.9 ms
SELL   7.66691 BTC @    81605  $     625,658  Alo   node->you  20.5 ms

Almost all large orders are post-only quotes. Traders re-send many of them at the same size a few ticks away. To track what a trader intends, follow each client order id through its cancels and modifies rather than reacting to every new order. For a production client, also cap the seen set and move the parsing to a process pool if one core cannot keep up.

Running the Mempool Stream in Production

Drain the stream continuously. The unfiltered feed is about 29 records and 4 to 5 MB per second, and single records reach about 2 MB. Set the client receive limit well above gRPC's 4 MiB default. A client that reads too slowly gets DEADLINE_EXCEEDED or DATA_LOSS and must reconnect.

Leave gzip off for the lowest latency. In our test, gzip raised the median gateway-to-client delay from 4 ms to 18 ms. Turn it on only when bandwidth costs more to you than 14 ms.

Resume with overlap and deduplicate. Each Dwellir Tokyo gateway stamps its own receive time. The same client block can therefore have a slightly different timestamp_ns on another gateway. Reconnect from a second before your last record and drop duplicates on tx_hash or client order id. The StreamMempoolTxs docs list the error codes and resume rules.

Use replay for research. Dwellir keeps about 9 to 12 hours of mempool history. A stream positioned 10 minutes back replayed at about 310 records per second and caught up with the live feed in 60 seconds. That is enough to rebuild a morning's order flow or to recover from a long disconnect.

Run in Tokyo. Dwellir serves the mempool feed from its Tokyo region only. A client in Europe or the US adds that network distance to every record and gives back part of the lead.

How Does Dwellir Compare With Other Hyperliquid Mempool Feeds?

OptionTransportServer-side coin filterReplayNotes
Dwellir StreamMempoolTxsgRPCNoAbout 9 to 12 hoursTokyo region. Same API key and protos as the other Dwellir Hyperliquid streams
QuickNode MEMPOOL_TXSgRPCYes (coin, coins)Not stated in the docsFull mempool firehose by default
0xArchive mempool channelWebSocketYes (symbol)None, live onlyPro plan and above. Pairs with its l4_orders channel
Your own non-validating nodeFiles on diskYour codeYour diskNeeds a gossip peer and 16+ cores. Mempool output adds about 0.5 TB of writes per day

QuickNode and 0xArchive filter on the server, which saves bandwidth if you follow one or two markets. 0xArchive's mempool docs also show how to recover the signing wallet from each action's signature and how to join pending orders to their outcomes. The same method works on Dwellir's records, because both include the action, nonce and signature exactly as signed.

Dwellir's stream suits teams that already use Hyperliquid gRPC for blocks, fills or order books and want the mempool on the same connection and key. Replay is the main difference from a live-only feed. After an outage you can reconnect and rebuild what you missed. If you would rather run the node yourself, Dwellir also offers Hyperliquid node peering in Tokyo.

Get Started

Hyperliquid's public API can only report what a block already decided. The mempool shows the request about a third of a second earlier. Hyperliquid runs cancels before GTC and IOC orders in a block. A cancel sent on that warning runs first if it reaches the same block.

To try it, enable the Hyperliquid gRPC add-on in the Dwellir dashboard and run the script above from a Tokyo host. For dedicated Tokyo capacity or a node colocated with your strategy, contact the Dwellir team. For how Dwellir's other Hyperliquid feeds compare on latency, see the Hyperliquid data latency benchmark.

read another blog post