GetOrderBookSnapshot
Retrieve a single order book snapshot at a specific point in time from the Hyperliquid L1 Gateway.
Full Code Examples
Clone our gRPC Code Examples Repository for complete, runnable implementations in Go, Python, and Node.js.
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_ms = 1;
}
The timestamp_ms field specifies the time in milliseconds since Unix epoch for the requested snapshot.
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 "hyperliquid-grpc-client/api/v2"
"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_ms 0 = latest)
request := &pb.Timestamp{TimestampMs: 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_ms=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, 'v2.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.v2.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_ms: 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#
- 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
Resources#
- GitHub: gRPC Code Examples - Complete working examples
- Copy Trading Bot - Production-ready trading bot example
Need help? Contact our support team or check the Hyperliquid gRPC documentation.