StreamOrderStatuses - Real-time Order Status Streaming
Stream order lifecycle events from Hyperliquid L1 Gateway via gRPC. Track opens, fills, cancellations, and rejections per block in real time.
Stream order status events starting from a position, providing real-time access to the lifecycle of every order on Hyperliquid: opens, fills, cancellations, and rejections.
Full Code Examples
Clone our gRPC Code Examples Repository for complete, runnable implementations in Go, Python, and Node.js.
When to Use This Method
StreamOrderStatuses is essential for:
- Order Lifecycle Tracking - Follow an order from open through fill, cancel, or rejection
- Rejection Analysis - Identify why orders are rejected using the
statusfield - Resting-Order Context - Read the full order object behind each status change
- Compliance and Auditing - Record every status transition with block alignment
Method Signature
rpc StreamOrderStatuses(Position) returns (stream BlockOrderStatuses) {}Response Stream
message BlockOrderStatuses {
// JSON-encoded object matching Hyperliquid's "node_order_statuses_by_block" format.
bytes data = 1;
}The data field contains a JSON-encoded block envelope with:
- Block reference (
local_time,block_time,block_number) - An
eventsarray of status records - A full order object behind each status change
Full Order Status Spec
StreamOrderStatuses emits a JSON payload matching Hyperliquid's node_order_statuses_by_block format. The envelope is the same block-envelope shape as fills, but events holds status-record objects rather than [address, fill] tuples.
Top-level keys (always present):
{
"local_time": "2026-04-20T00:00:00.040479928", // ISO-8601 timestamp when node processed the block (nanosecond precision)
"block_time": "2026-04-19T23:59:59.576040791", // ISO-8601 timestamp from block consensus
"block_number": 12345, // Block height containing these events
"events": [ // Array of status-record objects
{
"time": "2026-04-19T23:59:59.576040791", // Event timestamp (string, not integer ms)
"user": "0x1234567890abcdef1234567890abcdef12345678", // User address
"hash": "0x00...", // Transaction hash (nullable)
"builder": { "b": "0x00...", "f": 50 }, // Builder address and fee (nullable)
"status": "open", // Status value (see table below)
"order": { } // Full order object (see Order Object)
}
]
}Status event fields (events[i]):
| Field | Type | Description |
|---|---|---|
time | string | Event timestamp in the no-Z layout. Note: this is a string, not an integer millisecond value |
user | string | User address |
hash | string or null | Transaction hash. null for some statuses (for example reduceOnlyCanceled) |
builder | object or null | { "b": <address>, "f": <fee> }, the builder address and fee |
status | string | Status value. See Status Values |
order | object | The resting-order view. See Order Object |
Status Values
Observed status values (the list is not exhaustive):
| Status | Meaning |
|---|---|
open | Order accepted and resting on the book |
filled | Order filled |
canceled | Order canceled |
reduceOnlyCanceled | Reduce-only order canceled |
badAloPxRejected | Add-liquidity-only order rejected on price |
insufficientSpotBalanceRejected | Rejected for insufficient spot balance |
iocCancelRejected | Immediate-or-cancel order rejected |
minTradeNtlRejected | Rejected for failing minimum trade notional |
perpMarginRejected | Rejected for insufficient perp margin |
reduceOnlyRejected | Reduce-only order rejected |
selfTradeCanceled | Canceled to prevent a self-trade |
Order Object
The order field is the shared resting-order schema, the same object returned by GetOrderBookSnapshot.
| Field | Type | Description |
|---|---|---|
coin | string | Coin identifier |
side | string | "B" or "A" |
limitPx | string | Limit price (numeric string) |
sz | string | Current size (numeric string) |
oid | number | Order identifier. Not reliably unique across coins. See Order Identifiers |
timestamp | number | Order-creation time in Unix milliseconds |
triggerCondition | string | "N/A" for non-trigger orders |
isTrigger | boolean | Whether the order is a trigger order |
triggerPx | string | "0.0" when isTrigger is false |
children | array | Nested trigger-order schedule. Trigger orders (TP/SL) live here, not as top-level entries. Empty for non-trigger parents |
isPositionTpsl | boolean | Whether this is a position-level take-profit / stop-loss |
reduceOnly | boolean | Whether the order is reduce-only |
orderType | string | "Limit", "Trigger", etc. |
origSz | string | Original size |
tif | string | Time-in-force, for example "Alo", "Gtc", "Ioc" |
cloid | string or null | Client order ID |
Guarantees and alignment:
eventscontains all status records from the block, across all coins and users.- Messages arrive in strictly increasing
block_number, one per on-disk block (including blocks with an emptyeventsarray). block_numbercorresponds toabci_block.roundin StreamBlocks;block_timealigns withabci_block.time.
Developer tips:
- Branch on
statusto track lifecycle transitions and isolate rejections. - Handle
hashandbuilderdefensively; both can benull. - Treat
timeas a formatted string, distinct from the integer-millisecondtimeon fill events.
Order Identifiers
oid is not reliably unique across coins. Consumers that index or join per-order state must key on the (coin, oid) tuple, never on oid alone.
Outcome markets (HIP-4) demonstrably reuse oids across the two sides of a single market (for example #30 and #31) within one block. Keying on (coin, oid) is safe whether or not oids turn out to be globally unique elsewhere.
Common Use Cases
1. Rejection Monitor
REJECTIONS = {
'badAloPxRejected', 'insufficientSpotBalanceRejected', 'iocCancelRejected',
'minTradeNtlRejected', 'perpMarginRejected', 'reduceOnlyRejected',
}
def count_rejections(block):
counts = {}
for event in block.get('events', []):
status = event['status']
if status in REJECTIONS:
counts[status] = counts.get(status, 0) + 1
return counts2. Streaming Statuses in Python
import grpc
import json
import os
import hyperliquid_pb2
import hyperliquid_pb2_grpc
def stream_order_statuses():
endpoint = os.getenv('HYPERLIQUID_ENDPOINT')
api_key = os.getenv('API_KEY')
credentials = grpc.ssl_channel_credentials()
options = [('grpc.max_receive_message_length', 150 * 1024 * 1024)] # 150MB max
metadata = [('x-api-key', api_key)]
with grpc.secure_channel(endpoint, credentials, options=options) as channel:
client = hyperliquid_pb2_grpc.HyperliquidL1GatewayStub(channel)
# Empty Position means latest; set block_height or timestamp_ms to backfill
request = hyperliquid_pb2.Position()
for response in client.StreamOrderStatuses(request, metadata=metadata):
block = json.loads(response.data)
for event in block.get('events', []):
print(f"{event['status']:>24} {event['order']['coin']} oid={event['order']['oid']}")Error Handling and Reconnection
Reconnect by replaying from the last processed block. Pass the next block_height in the Position so no events are skipped or duplicated.
def stream_with_resume(client, metadata, start_block):
next_block = start_block
while True:
request = hyperliquid_pb2.Position(block_height=next_block)
try:
for response in client.StreamOrderStatuses(request, metadata=metadata):
block = json.loads(response.data)
process(block)
next_block = block['block_number'] + 1
except grpc.RpcError as e:
print(f"stream error, resuming from {next_block}: {e}")Best Practices
- Connection Management: Implement robust reconnection logic with exponential backoff
- Resumable State: Track the last processed
block_numberso you can resume from the next block - Stable Keys: Index per-order state on
(coin, oid)to stay correct across coins - Defensive Parsing: Handle nullable
hashandbuilderand the non-exhaustivestatusset - Resource Cleanup: Properly close streams and connections on shutdown
Current Limitations
- Backfill Window: Positioned requests are served from the node's on-disk feed; very old positions may fall outside the retained window. When the feed directory is not configured, positioned requests return
NotFoundand live tails end immediately. - Backpressure: High-volume periods may require careful handling to avoid overwhelming downstream systems
Resources
- GitHub: gRPC Code Examples - Complete working examples
- GetOrderStatuses - Point-in-time order status retrieval
- StreamRawBookDiffs - The sibling block-envelope stream for resting-book changes
- Archival Data - Historical
node_order_statuses_by_blockdownloads
Need help? Contact our support team or check the Hyperliquid gRPC documentation.
StreamFills - Real-time Fill Streaming
Stream continuous fill data from Hyperliquid L1 Gateway via gRPC. Monitor order executions in real-time.
StreamOrderbookSnapshots - Real-time Order Book Streaming
Stream continuous order book snapshots with individual order visibility from Hyperliquid L1 Gateway via gRPC. Premium endpoint available on dedicated clusters.