GetOrderBookSnapshot
Retrieve a single order book snapshot at a specific point in time from the Hyperliquid L1 Gateway.
When to Use This Method#
GetOrderBookSnapshot is essential for:
- Market Analysis - Get current market depth and liquidity
- Trading Strategies - Analyze bid/ask spreads and order distribution
- Price Discovery - Determine fair market value
- Risk Management - Assess market impact before placing orders
Method Signature#
rpc GetOrderBookSnapshot(Timestamp) returns (OrderBookSnapshot) {}
Request Message#
message Timestamp {
int64 timestamp = 1;
}
Current Limitation: The timestamp parameter is not yet implemented. This method currently returns the most recent order book snapshot regardless of the timestamp value provided.
Response Message#
message OrderBookSnapshot {
// JSON-encoded object conforming to a
// Hyperliquid order book snapshot
bytes data = 1;
}
The data field contains a JSON-encoded order book snapshot with the following structure:
Data Structure#
{
"coin": "string", // Trading pair symbol (e.g., "@1" for ETH)
"time": 1757672867000, // Unix timestamp in milliseconds
"levels": [
[ // Bid levels (index 0)
{
"px": "18.414", // Price level
"sz": "100.0", // Total size at this level
"n": 1 // Number of orders at this level
},
// ... more bid levels
],
[ // Ask levels (index 1)
{
"px": "18.515", // Price level
"sz": "50.5", // Total size at this level
"n": 2 // Number of orders at this level
},
// ... more ask levels
]
]
}
Field Descriptions#
| Field | Type | Description |
|---|---|---|
coin | string | The trading pair symbol |
time | number | Unix timestamp in milliseconds |
levels[0] | array | Bid orders sorted by price (descending) |
levels[1] | array | Ask orders sorted by price (ascending) |
px | string | Price at this level |
sz | string | Total size/volume at this price level |
n | number | Number of individual orders at this level |
Implementation Examples#
- Go
- Python
- Node.js
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"time"
pb "your-project/hyperliquid_l1_gateway/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)
type OrderBookLevel struct {
Price string `json:"px"`
Size string `json:"sz"`
Orders int `json:"n"`
}
type OrderBookData struct {
Coin string `json:"coin"`
Time float64 `json:"time"`
Levels [][]OrderBookLevel `json:"levels"`
}
func getOrderBookSnapshot(ctx context.Context, client pb.HyperLiquidL1GatewayClient) (*OrderBookData, error) {
// Create context with timeout
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// Request current snapshot (timestamp 0 = latest)
request := &pb.Timestamp{Timestamp: 0}
response, err := client.GetOrderBookSnapshot(ctx, request)
if err != nil {
return nil, fmt.Errorf("RPC call failed: %v", err)
}
fmt.Printf("📦 Response size: %d bytes\n\n", len(response.Data))
// Parse JSON response
var orderBook OrderBookData
if err := json.Unmarshal(response.Data, &orderBook); err != nil {
return nil, fmt.Errorf("failed to parse JSON: %v", err)
}
return &orderBook, nil
}
func main() {
endpoint := os.Getenv("HYPERLIQUID_ENDPOINT")
apiKey := os.Getenv("API_KEY")
if endpoint == "" {
log.Fatal("Error: HYPERLIQUID_ENDPOINT environment variable is required.\nPlease create a .env file and set your endpoint.")
}
if apiKey == "" {
log.Fatal("Error: API_KEY environment variable is required.\nPlease set your API key in the .env file.")
}
fmt.Println("🚀 Hyperliquid Go gRPC Client - Get Order Book Snapshot")
fmt.Println("======================================================")
fmt.Printf("📡 Endpoint: %s\n\n", endpoint)
// Set up TLS connection
creds := credentials.NewTLS(nil)
// Connection options
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(150 * 1024 * 1024), // 150MB max
),
}
fmt.Println("🔌 Connecting to gRPC server...")
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
log.Fatalf("❌ Failed to connect: %v", err)
}
defer conn.Close()
// Create the client
client := pb.NewHyperLiquidL1GatewayClient(conn)
fmt.Println("✅ Connected successfully!\n")
fmt.Println("📥 Fetching order book snapshot...\n")
// Get order book snapshot with API key
ctx := context.Background()
ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", apiKey)
orderBook, err := getOrderBookSnapshot(ctx, client)
if err != nil {
log.Fatalf("❌ Failed to get order book: %v", err)
}
// Process the response
processOrderBook(orderBook)
}
func processOrderBook(orderBook *OrderBookData) {
// Display basic information
fmt.Printf("Coin: %s\n", orderBook.Coin)
// Convert timestamp
timestamp := time.Unix(int64(orderBook.Time/1000), 0)
fmt.Printf("Time: %s\n", timestamp.Format("2006-01-02 15:04:05 UTC"))
if len(orderBook.Levels) >= 2 {
bids := orderBook.Levels[0]
asks := orderBook.Levels[1]
fmt.Printf("\nBids: %d levels\n", len(bids))
fmt.Printf("Asks: %d levels\n", len(asks))
// Display best bid and ask
if len(bids) > 0 && len(asks) > 0 {
fmt.Printf("\nBest Bid: %s @ %s (Orders: %d)\n",
bids[0].Size, bids[0].Price, bids[0].Orders)
fmt.Printf("Best Ask: %s @ %s (Orders: %d)\n",
asks[0].Size, asks[0].Price, asks[0].Orders)
// Calculate spread
spread := calculateSpread(bids[0].Price, asks[0].Price)
fmt.Printf("Spread: %.6f\n", spread)
}
// Display market depth (top 5 levels)
fmt.Println("\nMarket Depth (Top 5):")
displayDepth(bids, asks, 5)
}
}
func calculateSpread(bidPrice, askPrice string) float64 {
var bid, ask float64
fmt.Sscanf(bidPrice, "%f", &bid)
fmt.Sscanf(askPrice, "%f", &ask)
return ask - bid
}
func displayDepth(bids, asks []OrderBookLevel, levels int) {
maxLevels := min(levels, len(bids), len(asks))
fmt.Println(" Bids | Asks")
fmt.Println(" --------------- | ---------------")
for i := 0; i < maxLevels; i++ {
fmt.Printf(" %s @ %-8s | %s @ %s\n",
bids[i].Size, bids[i].Price,
asks[i].Size, asks[i].Price)
}
}
func min(values ...int) int {
minVal := values[0]
for _, v := range values[1:] {
if v < minVal {
minVal = v
}
}
return minVal
}
import grpc
import json
import sys
import os
from datetime import datetime
from dotenv import load_dotenv
import hyperliquid_pb2
import hyperliquid_pb2_grpc
# Load environment variables
load_dotenv()
def get_order_book_snapshot():
endpoint = os.getenv('HYPERLIQUID_ENDPOINT')
api_key = os.getenv('API_KEY')
if not endpoint:
print("Error: HYPERLIQUID_ENDPOINT environment variable is required.")
print("Please create a .env file from .env.example and set your endpoint.")
sys.exit(1)
if not api_key:
print("Error: API_KEY environment variable is required.")
print("Please set your API key in the .env file.")
sys.exit(1)
print('🚀 Hyperliquid Python gRPC Client - Get Order Book Snapshot')
print('=========================================================')
print(f'📡 Endpoint: {endpoint}\n')
# Create SSL credentials
credentials = grpc.ssl_channel_credentials()
# Create channel with options
options = [
('grpc.max_receive_message_length', 150 * 1024 * 1024), # 150MB max
]
# Prepare metadata with API key
metadata = [('x-api-key', api_key)]
print('🔌 Connecting to gRPC server...')
with grpc.secure_channel(endpoint, credentials, options=options) as channel:
# Create client stub
client = hyperliquid_pb2_grpc.HyperLiquidL1GatewayStub(channel)
print('✅ Connected successfully!\n')
# Create request - 0 means latest snapshot
request = hyperliquid_pb2.Timestamp(timestamp=0)
print('📥 Fetching order book snapshot...\n')
try:
# Get order book snapshot with metadata
response = client.GetOrderBookSnapshot(request, metadata=metadata)
print(f'📦 Response size: {len(response.data)} bytes\n')
# Process the snapshot
process_order_book(response.data)
except grpc.RpcError as e:
print(f'❌ RPC error: {e}')
except Exception as e:
print(f'❌ Error: {e}')
def process_order_book(data):
try:
# Parse JSON
order_book = json.loads(data.decode('utf-8'))
print('📊 ORDER BOOK SNAPSHOT')
print('===================')
# Display coin/symbol
if 'coin' in order_book:
print(f'🪙 Coin: {order_book["coin"]}')
# Display timestamp
if 'time' in order_book:
timestamp = order_book['time']
dt = datetime.fromtimestamp(timestamp / 1000)
print(f'⏰ Time: {dt.strftime("%Y-%m-%d %H:%M:%S UTC")}')
# Display levels
if 'levels' in order_book and isinstance(order_book['levels'], list):
if len(order_book['levels']) >= 2:
bids = order_book['levels'][0]
asks = order_book['levels'][1]
print(f'\n📋 Bids: {len(bids)} levels')
print(f'📋 Asks: {len(asks)} levels')
# Display best bid and ask
if len(bids) > 0 and len(asks) > 0:
best_bid = bids[0]
best_ask = asks[0]
print(f'\n💰 Best Bid: {best_bid["sz"]} @ {best_bid["px"]} (Orders: {best_bid["n"]})')
print(f'💰 Best Ask: {best_ask["sz"]} @ {best_ask["px"]} (Orders: {best_ask["n"]})')
# Calculate spread
bid_price = float(best_bid['px'])
ask_price = float(best_ask['px'])
spread = ask_price - bid_price
spread_bps = (spread / bid_price) * 10000
print(f'\n📊 Spread: {spread:.6f} ({spread_bps:.2f} bps)')
# Display top 5 levels
print('\n📈 ORDER BOOK (Top 5 Levels)')
print(' Bids | Asks')
print(' -------------------- | --------------------')
max_levels = min(5, len(bids), len(asks))
for i in range(max_levels):
bid = bids[i]
ask = asks[i]
print(f' {bid["sz"]:>8s} @ {bid["px"]:<9s} | {ask["sz"]:>8s} @ {ask["px"]:<9s}')
# Calculate depth
top_10_bids = bids[:10]
top_10_asks = asks[:10]
bid_depth = sum(float(level['sz']) for level in top_10_bids)
ask_depth = sum(float(level['sz']) for level in top_10_asks)
total_depth = bid_depth + ask_depth
print(f'\n📊 Market Depth (Top 10 levels):')
print(f' Bid depth: {bid_depth:.4f}')
print(f' Ask depth: {ask_depth:.4f}')
print(f' Total depth: {total_depth:.4f}')
if total_depth > 0:
imbalance = (bid_depth - ask_depth) / total_depth
print(f' Depth imbalance: {imbalance:.3f}')
except json.JSONDecodeError as e:
print(f'❌ Failed to parse JSON: {e}')
print(f'Raw data (first 200 bytes): {data[:200]}')
except Exception as e:
print(f'❌ Error processing order book: {e}')
if __name__ == '__main__':
get_order_book_snapshot()
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
require('dotenv').config();
// Load proto file
const PROTO_PATH = path.join(__dirname, 'hyperliquid.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const proto = grpc.loadPackageDefinition(packageDefinition);
// Main function
async function getOrderBookSnapshot() {
const endpoint = process.env.HYPERLIQUID_ENDPOINT;
const apiKey = process.env.API_KEY;
if (!endpoint) {
console.error('Error: HYPERLIQUID_ENDPOINT environment variable is required.');
console.error('Please create a .env file from .env.example and set your endpoint.');
process.exit(1);
}
if (!apiKey) {
console.error('Error: API_KEY environment variable is required.');
console.error('Please set your API key in the .env file.');
process.exit(1);
}
console.log('🚀 Hyperliquid Node.js gRPC Client - Get Order Book Snapshot');
console.log('===========================================================');
console.log(`📡 Endpoint: ${endpoint}\n`);
// Create metadata with API key
const metadata = new grpc.Metadata();
metadata.add('x-api-key', apiKey);
// Create client with SSL credentials and 150MB message size limit
const client = new proto.hyperliquid_l1_gateway.v1.HyperLiquidL1Gateway(
endpoint,
grpc.credentials.createSsl(),
{
'grpc.max_receive_message_length': 150 * 1024 * 1024
}
);
console.log('📥 Fetching order book snapshot...\n');
// Make the gRPC call
client.GetOrderBookSnapshot({ timestamp: 0 }, metadata, (error, response) => {
if (error) {
console.error('❌ gRPC error:', error.message);
return;
}
try {
const orderBook = JSON.parse(response.data);
console.log(`📦 Response size: ${response.data.length} bytes\n`);
// Process the order book
processOrderBook(orderBook);
} catch (error) {
console.error('❌ Failed to parse response:', error.message);
}
});
}
function processOrderBook(orderBook) {
console.log('📊 ORDER BOOK SNAPSHOT');
console.log('===================');
// Display coin/symbol
if (orderBook.coin) {
console.log(`🪙 Coin: ${orderBook.coin}`);
}
// Display timestamp
if (orderBook.time) {
const date = new Date(orderBook.time);
console.log(`⏰ Time: ${date.toISOString()}`);
}
// Display levels
const levels = orderBook.levels || [];
if (levels.length >= 2) {
const bids = levels[0];
const asks = levels[1];
console.log(`\n📋 Bids: ${bids.length} levels`);
console.log(`📋 Asks: ${asks.length} levels`);
// Display best bid and ask
if (bids.length > 0 && asks.length > 0) {
const bestBid = bids[0];
const bestAsk = asks[0];
console.log(`\n💰 Best Bid: ${bestBid.sz} @ ${bestBid.px} (Orders: ${bestBid.n})`);
console.log(`💰 Best Ask: ${bestAsk.sz} @ ${bestAsk.px} (Orders: ${bestAsk.n})`);
// Calculate spread
const bidPrice = parseFloat(bestBid.px);
const askPrice = parseFloat(bestAsk.px);
const spread = askPrice - bidPrice;
const spreadBps = (spread / bidPrice) * 10000;
console.log(`\n📊 Spread: ${spread.toFixed(6)} (${spreadBps.toFixed(2)} bps)`);
}
// Display top 5 levels
console.log('\n📈 ORDER BOOK (Top 5 Levels)');
console.log(' Bids | Asks');
console.log(' -------------------- | --------------------');
const maxLevels = Math.min(5, bids.length, asks.length);
for (let i = 0; i < maxLevels; i++) {
const bid = bids[i];
const ask = asks[i];
const bidStr = `${bid.sz.padStart(8)} @ ${bid.px.padEnd(9)}`;
const askStr = `${ask.sz.padStart(8)} @ ${ask.px.padEnd(9)}`;
console.log(` ${bidStr} | ${askStr}`);
}
// Calculate depth
const top10Bids = bids.slice(0, 10);
const top10Asks = asks.slice(0, 10);
const bidDepth = top10Bids.reduce((sum, level) => sum + parseFloat(level.sz), 0);
const askDepth = top10Asks.reduce((sum, level) => sum + parseFloat(level.sz), 0);
const totalDepth = bidDepth + askDepth;
console.log('\n📊 Market Depth (Top 10 levels):');
console.log(` Bid depth: ${bidDepth.toFixed(4)}`);
console.log(` Ask depth: ${askDepth.toFixed(4)}`);
console.log(` Total depth: ${totalDepth.toFixed(4)}`);
if (totalDepth > 0) {
const imbalance = (bidDepth - askDepth) / totalDepth;
console.log(` Depth imbalance: ${imbalance.toFixed(3)}`);
}
}
}
// Run it
getOrderBookSnapshot();
Common Use Cases#
1. Market Making Strategy#
async function marketMakingSignals(client) {
const orderBook = await client.getSnapshot();
const metrics = client.calculateMetrics(orderBook);
if (!metrics) return null;
// Calculate fair value and optimal spread
const midPrice = (metrics.bestBid + metrics.bestAsk) / 2;
const optimalSpread = Math.max(metrics.spread * 0.8, 0.001); // Minimum spread
// Analyze order book imbalance for pricing adjustment
const imbalanceAdjustment = metrics.depthImbalance * 0.0001; // Adjust based on imbalance
return {
fairValue: midPrice,
bidPrice: midPrice - (optimalSpread / 2) + imbalanceAdjustment,
askPrice: midPrice + (optimalSpread / 2) + imbalanceAdjustment,
marketDepth: metrics.totalVolume,
spreadTightness: metrics.spreadBps,
imbalance: metrics.depthImbalance,
bidOrders: metrics.bidOrders,
askOrders: metrics.askOrders
};
}
2. Liquidity Assessment#
def assess_liquidity(order_book, target_size):
"""Assess market impact for a given trade size"""
levels = order_book.get('levels', [])
if len(levels) < 2:
return None
bids = levels[0] # Bid levels
asks = levels[1] # Ask levels
def calculate_slippage(levels, size, is_buy):
filled = 0
weighted_price = 0
orders_hit = 0
for level in levels:
price = float(level['px'])
volume = float(level['sz'])
num_orders = level['n']
fill_amount = min(size - filled, volume)
weighted_price += price * fill_amount
filled += fill_amount
orders_hit += num_orders
if filled >= size:
break
return {
'avg_price': weighted_price / filled if filled > 0 else None,
'filled': filled,
'orders_hit': orders_hit
}
# Calculate slippage for buy and sell
buy_impact = calculate_slippage(asks, target_size, True) # Buy from asks
sell_impact = calculate_slippage(bids, target_size, False) # Sell to bids
if not bids or not asks:
return None
best_bid = float(bids[0]['px'])
best_ask = float(asks[0]['px'])
mid_price = (best_bid + best_ask) / 2
result = {
'mid_price': mid_price,
'target_size': target_size,
'can_buy': buy_impact['filled'] >= target_size,
'can_sell': sell_impact['filled'] >= target_size
}
if buy_impact['avg_price']:
result['buy_price'] = buy_impact['avg_price']
result['buy_slippage'] = (buy_impact['avg_price'] - mid_price) / mid_price
result['buy_orders_hit'] = buy_impact['orders_hit']
if sell_impact['avg_price']:
result['sell_price'] = sell_impact['avg_price']
result['sell_slippage'] = (mid_price - sell_impact['avg_price']) / mid_price
result['sell_orders_hit'] = sell_impact['orders_hit']
return result
3. Real-time Price Display#
func displayOrderBookLadder(orderBook *OrderBookData) {
fmt.Println("\n=== ORDER BOOK LADDER ===")
fmt.Printf("Coin: %s\n", orderBook.Coin)
// Convert and display timestamp
timestamp := time.Unix(int64(orderBook.Time/1000), 0)
fmt.Printf("Time: %s\n", timestamp.Format("15:04:05 UTC"))
if len(orderBook.Levels) < 2 {
fmt.Println("No order book data available")
return
}
bids := orderBook.Levels[0]
asks := orderBook.Levels[1]
// Display top 5 asks in reverse order (highest price first)
fmt.Println("\n ASKS")
fmt.Println(" Orders | Size | Price")
fmt.Println(" -------|-------------|------------")
displayCount := min(5, len(asks))
for i := displayCount - 1; i >= 0; i-- {
ask := asks[i]
fmt.Printf(" %6d | %11s | %10s\n",
ask.Orders, ask.Size, ask.Price)
}
// Display spread line
fmt.Println(" ======================================")
if len(bids) > 0 && len(asks) > 0 {
spread := calculateSpread(bids[0].Price, asks[0].Price)
fmt.Printf(" SPREAD: %.6f\n", spread)
}
fmt.Println(" ======================================")
// Display top 5 bids
fmt.Println(" BIDS")
fmt.Println(" Orders | Size | Price")
fmt.Println(" -------|-------------|------------")
for i := 0; i < min(5, len(bids)); i++ {
bid := bids[i]
fmt.Printf(" %6d | %11s | %10s\n",
bid.Orders, bid.Size, bid.Price)
}
// Display summary
fmt.Printf("\nTotal Levels: %d bids, %d asks\n",
len(bids), len(asks))
}
Error Handling#
async function robustGetSnapshot(client, retries = 3) {
for (let attempt = 0; attempt < retries; attempt++) {
try {
return await client.getSnapshot();
} catch (error) {
console.warn(`Attempt ${attempt + 1} failed:`, error.message);
if (attempt === retries - 1) {
throw error;
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
Best Practices#
- Cache Management: Order book snapshots can be cached briefly (1-5 seconds) for non-critical applications
- Data Validation: Always validate JSON structure and numerical values
- Error Recovery: Implement retry logic with exponential backoff
- Resource Management: Close gRPC connections properly to avoid resource leaks
- Performance: For high-frequency updates, consider using the streaming methods instead
Current Limitations#
- Historical Data: Timestamp parameter is not implemented; only current snapshots available
- Data Retention: Historical order book data is limited to a 24-hour rolling window
- Rate Limits: Be mindful of request frequency to avoid overwhelming the service
Need help? Contact our support team or check the Hyperliquid gRPC documentation.