GetBlock
Retrieve a single block at a specific position 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#
GetBlock is essential for:
- Point-in-Time Analysis - Get blockchain state at a specific block or timestamp
- Backtesting - Retrieve historical block data for strategy analysis
- Auditing - Verify transactions and state changes at specific points
- Debugging - Investigate specific blocks during development
Method Signature#
rpc GetBlock(Position) returns (Block) {}
Request Message#
message Position {
// Leave all fields unset or zero to target the latest data.
oneof position {
int64 timestamp_ms = 1; // ms since Unix epoch, inclusive
int64 block_height = 2; // block height, inclusive
}
}
The Position message allows flexible block targeting:
- timestamp_ms: Get block at or after a specific time (milliseconds since Unix epoch)
- block_height: Get block at a specific height
- Empty/zero: Get the latest block
Response Message#
message Block {
// JSON-encoded Hyperliquid block from "replica_cmds".
bytes data = 1;
}
The data field contains a JSON-encoded block with the following structure:
Data Structure#
{
"abci_block": {
"time": "2025-09-08T06:41:57.997372546",
"round": 992814678,
"parent_round": 992814677,
"proposer": "0x5ac99df645f3414876c816caa18b2d234024b487",
"hardfork": {
"version": 57,
"round": 990500929
},
"signed_action_bundles": [
[
"0xb4b1f5a9c233f9d90fd24b9961fd12708b36cc3d56f8fda47f32b667ee8d1227",
{
"signed_actions": [
{
"signature": {
"r": "0x...",
"s": "0x...",
"v": 27
},
"action": {
"type": "order",
"orders": [...]
},
"nonce": 1757313597362
}
],
"broadcaster": "0x67e451964e0421f6e7d07be784f35c530667c2b3",
"broadcaster_nonce": 1757313597367
}
]
]
},
"resps": {
"Full": [
[
"0xb4b1f5a9c233f9d90fd24b9961fd12708b36cc3d56f8fda47f32b667ee8d1227",
[
{
"user": "0xecb63caa47c7c4e77f60f1ce858cf28dc2b82b00",
"res": {
"status": "ok",
"response": {
"type": "order",
"data": {...}
}
}
}
]
]
]
}
}
Field Descriptions#
| Field | Type | Description |
|---|---|---|
abci_block.time | string | ISO-8601 timestamp with nanosecond precision |
abci_block.round | number | Current block number (height) |
abci_block.parent_round | number | Previous block number |
abci_block.proposer | string | Validator address that proposed the block |
abci_block.hardfork | object | Protocol version information |
abci_block.signed_action_bundles | array | Array of [hash, bundle_data] pairs |
resps.Full | array | Execution responses matching bundle structure |
For the complete block specification including all action types, see the StreamBlocks documentation.
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 BlockData struct {
AbciBlock struct {
Time string `json:"time"`
Round int64 `json:"round"`
ParentRound int64 `json:"parent_round"`
Proposer string `json:"proposer"`
SignedActionBundles []interface{} `json:"signed_action_bundles"`
} `json:"abci_block"`
Resps struct {
Full []interface{} `json:"Full"`
} `json:"resps"`
}
func getBlock(ctx context.Context, client pb.HyperliquidL1GatewayClient, position *pb.Position) (*BlockData, error) {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
response, err := client.GetBlock(ctx, position)
if err != nil {
return nil, fmt.Errorf("RPC call failed: %v", err)
}
fmt.Printf("Response size: %d bytes\n\n", len(response.Data))
var block BlockData
if err := json.Unmarshal(response.Data, &block); err != nil {
return nil, fmt.Errorf("failed to parse JSON: %v", err)
}
return &block, nil
}
func main() {
endpoint := os.Getenv("HYPERLIQUID_ENDPOINT")
apiKey := os.Getenv("API_KEY")
if endpoint == "" {
log.Fatal("Error: HYPERLIQUID_ENDPOINT environment variable is required.")
}
if apiKey == "" {
log.Fatal("Error: API_KEY environment variable is required.")
}
fmt.Println("Hyperliquid Go gRPC Client - Get Block")
fmt.Println("======================================")
fmt.Printf("Endpoint: %s\n\n", endpoint)
// Set up TLS connection
creds := credentials.NewTLS(nil)
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(150 * 1024 * 1024),
),
}
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()
client := pb.NewHyperliquidL1GatewayClient(conn)
fmt.Println("Connected successfully!\n")
// Get block with API key
ctx := context.Background()
ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", apiKey)
// Request latest block (empty position)
request := &pb.Position{}
// Or request by block height:
// request := &pb.Position{Position: &pb.Position_BlockHeight{BlockHeight: 992814678}}
// Or request by timestamp:
// request := &pb.Position{Position: &pb.Position_TimestampMs{TimestampMs: 1725778917997}}
fmt.Println("Fetching block...\n")
block, err := getBlock(ctx, client, request)
if err != nil {
log.Fatalf("Failed to get block: %v", err)
}
// Display block information
fmt.Printf("Block Height: %d\n", block.AbciBlock.Round)
fmt.Printf("Parent Block: %d\n", block.AbciBlock.ParentRound)
fmt.Printf("Time: %s\n", block.AbciBlock.Time)
fmt.Printf("Proposer: %s\n", block.AbciBlock.Proposer)
fmt.Printf("Action Bundles: %d\n", len(block.AbciBlock.SignedActionBundles))
}
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_dotenv()
def get_block(block_height=None, timestamp_ms=None):
endpoint = os.getenv('HYPERLIQUID_ENDPOINT')
api_key = os.getenv('API_KEY')
if not endpoint:
print("Error: HYPERLIQUID_ENDPOINT environment variable is required.")
sys.exit(1)
if not api_key:
print("Error: API_KEY environment variable is required.")
sys.exit(1)
print('Hyperliquid Python gRPC Client - Get Block')
print('==========================================')
print(f'Endpoint: {endpoint}\n')
credentials = grpc.ssl_channel_credentials()
options = [
('grpc.max_receive_message_length', 150 * 1024 * 1024),
]
metadata = [('x-api-key', api_key)]
print('Connecting to gRPC server...')
with grpc.secure_channel(endpoint, credentials, options=options) as channel:
client = hyperliquid_pb2_grpc.HyperliquidL1GatewayStub(channel)
print('Connected successfully!\n')
# Create request based on parameters
if block_height is not None:
request = hyperliquid_pb2.Position(block_height=block_height)
print(f'Requesting block at height: {block_height}')
elif timestamp_ms is not None:
request = hyperliquid_pb2.Position(timestamp_ms=timestamp_ms)
print(f'Requesting block at timestamp: {timestamp_ms}')
else:
request = hyperliquid_pb2.Position()
print('Requesting latest block')
print('\nFetching block...\n')
try:
response = client.GetBlock(request, metadata=metadata)
print(f'Response size: {len(response.data)} bytes\n')
process_block(response.data)
except grpc.RpcError as e:
print(f'RPC error: {e}')
except Exception as e:
print(f'Error: {e}')
def process_block(data):
try:
block = json.loads(data.decode('utf-8'))
print('BLOCK DATA')
print('==========')
abci_block = block.get('abci_block', {})
# Display block info
print(f'Block Height: {abci_block.get("round")}')
print(f'Parent Block: {abci_block.get("parent_round")}')
print(f'Time: {abci_block.get("time")}')
print(f'Proposer: {abci_block.get("proposer")}')
# Display hardfork info
hardfork = abci_block.get('hardfork', {})
print(f'\nHardfork Version: {hardfork.get("version")}')
print(f'Hardfork Round: {hardfork.get("round")}')
# Count action bundles
bundles = abci_block.get('signed_action_bundles', [])
print(f'\nAction Bundles: {len(bundles)}')
# Count total actions
total_actions = 0
action_types = {}
for bundle in bundles:
if len(bundle) >= 2:
bundle_data = bundle[1]
signed_actions = bundle_data.get('signed_actions', [])
total_actions += len(signed_actions)
for action in signed_actions:
action_type = action.get('action', {}).get('type', 'unknown')
action_types[action_type] = action_types.get(action_type, 0) + 1
print(f'Total Actions: {total_actions}')
if action_types:
print('\nAction Types:')
for action_type, count in sorted(action_types.items(), key=lambda x: -x[1]):
print(f' {action_type}: {count}')
except json.JSONDecodeError as e:
print(f'Failed to parse JSON: {e}')
except Exception as e:
print(f'Error processing block: {e}')
if __name__ == '__main__':
# Get latest block
get_block()
# Or get by block height:
# get_block(block_height=992814678)
# Or get by timestamp:
# get_block(timestamp_ms=1725778917997)
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
require('dotenv').config();
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);
async function getBlock(options = {}) {
const endpoint = process.env.HYPERLIQUID_ENDPOINT;
const apiKey = process.env.API_KEY;
if (!endpoint) {
console.error('Error: HYPERLIQUID_ENDPOINT environment variable is required.');
process.exit(1);
}
if (!apiKey) {
console.error('Error: API_KEY environment variable is required.');
process.exit(1);
}
console.log('Hyperliquid Node.js gRPC Client - Get Block');
console.log('===========================================');
console.log(`Endpoint: ${endpoint}\n`);
const metadata = new grpc.Metadata();
metadata.add('x-api-key', apiKey);
const client = new proto.hyperliquid_l1_gateway.v2.HyperliquidL1Gateway(
endpoint,
grpc.credentials.createSsl(),
{
'grpc.max_receive_message_length': 150 * 1024 * 1024
}
);
// Build request
const request = {};
if (options.blockHeight) {
request.block_height = options.blockHeight;
console.log(`Requesting block at height: ${options.blockHeight}`);
} else if (options.timestampMs) {
request.timestamp_ms = options.timestampMs;
console.log(`Requesting block at timestamp: ${options.timestampMs}`);
} else {
console.log('Requesting latest block');
}
console.log('\nFetching block...\n');
client.GetBlock(request, metadata, (error, response) => {
if (error) {
console.error('gRPC error:', error.message);
return;
}
try {
const block = JSON.parse(response.data);
console.log(`Response size: ${response.data.length} bytes\n`);
processBlock(block);
} catch (error) {
console.error('Failed to parse response:', error.message);
}
});
}
function processBlock(block) {
console.log('BLOCK DATA');
console.log('==========');
const abciBlock = block.abci_block || {};
console.log(`Block Height: ${abciBlock.round}`);
console.log(`Parent Block: ${abciBlock.parent_round}`);
console.log(`Time: ${abciBlock.time}`);
console.log(`Proposer: ${abciBlock.proposer}`);
// Hardfork info
const hardfork = abciBlock.hardfork || {};
console.log(`\nHardfork Version: ${hardfork.version}`);
console.log(`Hardfork Round: ${hardfork.round}`);
// Count bundles and actions
const bundles = abciBlock.signed_action_bundles || [];
console.log(`\nAction Bundles: ${bundles.length}`);
let totalActions = 0;
const actionTypes = {};
for (const bundle of bundles) {
if (bundle.length >= 2) {
const bundleData = bundle[1];
const signedActions = bundleData.signed_actions || [];
totalActions += signedActions.length;
for (const action of signedActions) {
const actionType = action.action?.type || 'unknown';
actionTypes[actionType] = (actionTypes[actionType] || 0) + 1;
}
}
}
console.log(`Total Actions: ${totalActions}`);
if (Object.keys(actionTypes).length > 0) {
console.log('\nAction Types:');
const sorted = Object.entries(actionTypes).sort((a, b) => b[1] - a[1]);
for (const [type, count] of sorted) {
console.log(` ${type}: ${count}`);
}
}
}
// Get latest block
getBlock();
// Or get by block height:
// getBlock({ blockHeight: 992814678 });
// Or get by timestamp:
// getBlock({ timestampMs: 1725778917997 });
Common Use Cases#
1. Historical Block Analysis#
def analyze_block_activity(client, block_height):
"""Analyze trading activity in a specific block"""
request = hyperliquid_pb2.Position(block_height=block_height)
response = client.GetBlock(request, metadata=metadata)
block = json.loads(response.data)
abci_block = block.get('abci_block', {})
bundles = abci_block.get('signed_action_bundles', [])
analysis = {
'block_height': abci_block.get('round'),
'timestamp': abci_block.get('time'),
'total_bundles': len(bundles),
'total_actions': 0,
'order_count': 0,
'cancel_count': 0,
'transfer_count': 0
}
for bundle in bundles:
if len(bundle) >= 2:
for action in bundle[1].get('signed_actions', []):
analysis['total_actions'] += 1
action_type = action.get('action', {}).get('type', '')
if action_type == 'order':
analysis['order_count'] += 1
elif action_type in ['cancel', 'cancelByCloid']:
analysis['cancel_count'] += 1
elif action_type in ['spotSend', 'usdSend', 'withdraw3']:
analysis['transfer_count'] += 1
return analysis
2. Block Comparison#
func compareBlocks(client pb.HyperliquidL1GatewayClient, ctx context.Context, height1, height2 int64) {
block1, _ := client.GetBlock(ctx, &pb.Position{Position: &pb.Position_BlockHeight{BlockHeight: height1}})
block2, _ := client.GetBlock(ctx, &pb.Position{Position: &pb.Position_BlockHeight{BlockHeight: height2}})
var data1, data2 map[string]interface{}
json.Unmarshal(block1.Data, &data1)
json.Unmarshal(block2.Data, &data2)
abci1 := data1["abci_block"].(map[string]interface{})
abci2 := data2["abci_block"].(map[string]interface{})
bundles1 := abci1["signed_action_bundles"].([]interface{})
bundles2 := abci2["signed_action_bundles"].([]interface{})
fmt.Printf("Block %d: %d bundles\n", height1, len(bundles1))
fmt.Printf("Block %d: %d bundles\n", height2, len(bundles2))
}
3. Transaction Verification#
async function verifyTransaction(client, metadata, txHash, blockHeight) {
return new Promise((resolve, reject) => {
const request = { block_height: blockHeight };
client.GetBlock(request, metadata, (error, response) => {
if (error) {
reject(error);
return;
}
const block = JSON.parse(response.data);
const bundles = block.abci_block?.signed_action_bundles || [];
for (const bundle of bundles) {
const bundleHash = bundle[0];
if (bundleHash === txHash) {
resolve({
found: true,
block_height: block.abci_block.round,
bundle_data: bundle[1]
});
return;
}
}
resolve({ found: false });
});
});
}
Error Handling#
async function robustGetBlock(client, metadata, position, retries = 3) {
for (let attempt = 0; attempt < retries; attempt++) {
try {
return await new Promise((resolve, reject) => {
client.GetBlock(position, metadata, (error, response) => {
if (error) reject(error);
else resolve(response);
});
});
} 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#
- Position Selection: Use block height for precise queries; timestamps may return different blocks across requests if near block boundaries
- Data Validation: Always validate JSON structure before processing
- Error Recovery: Implement retry logic with exponential backoff
- Resource Management: Close gRPC connections properly to avoid resource leaks
- Caching: Cache block data when analyzing the same blocks multiple times
Current Limitations#
- Data Retention: Historical block 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
- StreamBlocks Documentation - Full block specification and action types reference
Need help? Contact our support team or check the Hyperliquid gRPC documentation.