⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

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

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"
"time"

pb "your-project/hyperliquid_l1_gateway/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

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(client pb.HyperLiquidL1GatewayClient) (*OrderBookData, error) {
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 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)
}

// 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() {
const endpoint = "subsquid-hyperliquid-mainnet.n.dwellir.com:443"

// 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
),
}

// Connect to 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)

// Get order book snapshot
orderBook, err := getOrderBookSnapshot(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

  • 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.