Skip to main content

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#

FieldTypeDescription
coinstringThe trading pair symbol
timenumberUnix timestamp in milliseconds
levels[0]arrayBid orders sorted by price (descending)
levels[1]arrayAsk orders sorted by price (ascending)
pxstringPrice at this level
szstringTotal size/volume at this price level
nnumberNumber of individual orders at this level

Implementation Examples#

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
}

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#

  1. Cache Management: Order book snapshots can be cached briefly (1-5 seconds) for non-critical applications
  2. Data Validation: Always validate JSON structure and numerical values
  3. Error Recovery: Implement retry logic with exponential backoff
  4. Resource Management: Close gRPC connections properly to avoid resource leaks
  5. 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#

Need help? Contact our support team or check the Hyperliquid gRPC documentation.