Skip to content

Connecting

Aleatoric supports five transport methods for accessing Hyperliquid L1 data: two SDKs (Python, TypeScript) and three direct protocols (JSON-RPC, gRPC, Unified Stream SSE, and Disk-Sync WebSocket). This page provides comprehensive, working examples for each one.

All examples assume you have an API key. See Authentication for key provisioning and security details.

The following diagram shows how each transport maps to the Aleatoric data plane:

Transport Architecture

Each transport connects through the Aleatoric gateway, which handles authentication, rate limiting, and region routing before forwarding requests to the appropriate data service. The transport you choose determines the latency profile, message format, and streaming capabilities available to your application.

The fastest path to streaming data. Install with:

Terminal window
pip install hypercore-sdk

The SDK reads your API key from the ALEATORIC_API_KEY environment variable by default, or you can pass it explicitly.

from hypercore_sdk import HyperCoreAPI
# Uses ALEATORIC_API_KEY env var by default
with HyperCoreAPI() as api:
# Block queries
block = api.block_number()
print(f"Latest block: {block}")
# Mid price lookups
btc_mid = api.coin_mid("BTC")
eth_mid = api.coin_mid("ETH")
print(f"BTC mid: {btc_mid}, ETH mid: {eth_mid}")
# Account balance
balance = api.get_balance("0x...")
print(f"Balance: {balance}")
import asyncio
from hypercore_sdk import GrpcClient
async def stream_prices():
async with GrpcClient() as grpc:
# Stream BTC mid prices (server-streaming RPC)
async for price in grpc.mid_price_stream("BTC", max_events=100):
print(f"BTC mid: {price.price} @ {price.timestamp}")
# Stream multiple coins concurrently
async for price in grpc.mid_price_stream("ETH", max_events=50):
print(f"ETH mid: {price.price}")
asyncio.run(stream_prices())

For long-running streams, the SDK handles reconnection automatically. The reconnect strategy uses exponential backoff with jitter:

tretry=min ⁣(tmax,  tbase2n+Uniform(0,tjitter))t_{\text{retry}} = \min\!\Big(t_{\text{max}},\; t_{\text{base}} \cdot 2^{n} + \text{Uniform}(0, t_{\text{jitter}})\Big)

where tbase=1st_{\text{base}} = 1\text{s}, tmax=60st_{\text{max}} = 60\text{s}, tjitter=0.5st_{\text{jitter}} = 0.5\text{s}, and nn is the retry count.

from hypercore_sdk import UnifiedStreamClient
client = UnifiedStreamClient()
# Stream all events
for event in client.sse_events(max_events=50):
print(f"{event['event_type']}: {event['payload']}")
# Filter to specific event types
for event in client.sse_events(types=["trade", "liquidation_warning"], max_events=20):
coin = event["payload"].get("coin", "unknown")
print(f"[{event['event_type']}] {coin}: {event['payload']}")
# REST endpoints for point-in-time queries
stats = client.stats()
print(f"Stream stats: {stats}")
events = client.events(event_type="trade", limit=10)
for e in events:
print(f"Recent trade: {e}")
from hypercore_sdk import HyperCoreAPI, GrpcClient
# Pass key explicitly (overrides env var)
with HyperCoreAPI(api_key="ak_live_YOUR_KEY_HERE") as api:
print(api.block_number())
# Or set the endpoint for Japan region
with HyperCoreAPI(
api_key="ak_live_YOUR_KEY_HERE",
rpc_url="https://rpc-jp.aleatoric.systems",
) as api:
print(api.block_number())

Install with:

Terminal window
npm install hypercore-ts-sdk
import { HyperCoreAPI } from 'hypercore-ts-sdk';
const api = new HyperCoreAPI({
apiKey: process.env.ALEATORIC_API_KEY,
});
// Block queries
const block = await api.blockNumber();
console.log('Latest block:', block);
// Mid price lookups
const btcMid = await api.coinMid('BTC');
const ethMid = await api.coinMid('ETH');
console.log(`BTC: ${btcMid}, ETH: ${ethMid}`);
import { GrpcClient } from 'hypercore-ts-sdk';
const grpc = new GrpcClient({
apiKey: process.env.ALEATORIC_API_KEY,
});
// Stream BTC mid prices with callback
await grpc.streamMids('BTC', (update) => {
console.log(`BTC mid: ${update.price} @ ${update.timestamp}`);
}, { maxEvents: 100 });
// Async iterator pattern
for await (const update of grpc.midPriceIterator('ETH')) {
console.log(`ETH mid: ${update.price}`);
}
import { UnifiedStreamClient } from 'hypercore-ts-sdk';
const unified = new UnifiedStreamClient({
apiKey: process.env.ALEATORIC_API_KEY,
});
// REST queries
const stats = await unified.stats();
console.log('Stream stats:', stats);
const recentTrades = await unified.events({
event_type: 'trade',
limit: 10,
});
recentTrades.forEach((e) => console.log('Trade:', e));
// SSE streaming
const stream = unified.stream({ types: ['trade', 'funding'] });
stream.on('event', (event) => {
console.log(`[${event.event_type}]`, event.payload);
});
stream.on('error', (err) => console.error('Stream error:', err));
stream.connect();

Standard EVM-compatible JSON-RPC over HTTPS. No SDK required — any HTTP client works.

Terminal window
# Get latest block number
curl -s -X POST https://rpc.aleatoric.systems \
-H "Content-Type: application/json" \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# Response:
# {"jsonrpc":"2.0","id":1,"result":"0x1a4b3f"}
Terminal window
# Get account balance
curl -s -X POST https://rpc.aleatoric.systems \
-H "Content-Type: application/json" \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xYOUR_ADDRESS", "latest"],
"id": 2
}'
Terminal window
# Get transaction by hash
curl -s -X POST https://rpc.aleatoric.systems \
-H "Content-Type: application/json" \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
"params": ["0xTXN_HASH"],
"id": 3
}'
import { JsonRpcProvider, FetchRequest } from 'ethers';
const API_KEY = process.env.ALEATORIC_API_KEY;
// Custom FetchRequest to inject the API key header
const fetchReq = new FetchRequest('https://rpc.aleatoric.systems');
fetchReq.setHeader('x-api-key', API_KEY);
const provider = new JsonRpcProvider(fetchReq, undefined, {
staticNetwork: true,
batchMaxCount: 1,
});
const blockNumber = await provider.getBlockNumber();
console.log('Block:', blockNumber);
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
// Subscribe to new blocks (polling)
provider.on('block', (block) => {
console.log('New block:', block);
});
from web3 import Web3
from web3.providers.rpc import HTTPProvider
API_KEY = "ak_live_YOUR_KEY_HERE"
provider = HTTPProvider(
"https://rpc.aleatoric.systems",
request_kwargs={
"headers": {"x-api-key": API_KEY}
},
)
w3 = Web3(provider)
print(f"Connected: {w3.is_connected()}")
print(f"Block: {w3.eth.block_number}")
print(f"Balance: {w3.eth.get_balance('0x...')}")

For systems that connect directly via Protocol Buffers without the SDK. Download the proto definitions from the gRPC Reference.

Terminal window
# Health check (no auth required)
grpcurl \
hl.grpc.aleatoric.systems:443 \
grpc.health.v1.Health/Check
# Get BTC mid price
grpcurl \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
hl.grpc.aleatoric.systems:443 \
hyperliquid.PriceService/GetMidPrice \
-d '{"coin": "BTC"}'
# Stream mid prices (server-streaming)
grpcurl \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
hl.grpc.aleatoric.systems:443 \
hyperliquid.PriceService/StreamMids \
-d '{"coin": "BTC", "subscription": "allMids"}'
# List available services
grpcurl \
-H "x-api-key: ak_live_YOUR_KEY_HERE" \
hl.grpc.aleatoric.systems:443 \
list
import grpc
from hypercore_bridge_pb2_grpc import PriceServiceStub
from hypercore_bridge_pb2 import (
MidPriceRequest,
StreamMidsRequest,
L2BookRequest,
)
API_KEY = "ak_live_YOUR_KEY_HERE"
TARGET = "hl.grpc.aleatoric.systems:443"
# TLS channel (required — plaintext is not supported)
channel = grpc.secure_channel(TARGET, grpc.ssl_channel_credentials())
stub = PriceServiceStub(channel)
metadata = [("x-api-key", API_KEY)]
# Unary: get current BTC mid price
mid = stub.GetMidPrice(MidPriceRequest(coin="BTC"), metadata=metadata)
print(f"BTC mid: {mid.price}")
# Unary: get L2 order book snapshot
book = stub.GetL2Book(
L2BookRequest(coin="BTC", depth=10),
metadata=metadata,
)
for level in book.bids:
print(f" Bid: {level.price} x {level.size}")
for level in book.asks:
print(f" Ask: {level.price} x {level.size}")
# Server-streaming: subscribe to live prices
stream = stub.StreamMids(
StreamMidsRequest(coin="BTC", subscription="allMids"),
metadata=metadata,
)
for update in stream:
print(f"BTC: {update.price} (t={update.timestamp})")
package main
import (
"context"
"fmt"
"log"
pb "github.com/aleatoric-systems/hypercore-bridge/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)
func main() {
creds := credentials.NewClientTLSFromCert(nil, "")
conn, err := grpc.Dial(
"hl.grpc.aleatoric.systems:443",
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := pb.NewPriceServiceClient(conn)
// Attach API key metadata
ctx := metadata.AppendToOutgoingContext(
context.Background(),
"x-api-key", "ak_live_YOUR_KEY_HERE",
)
// Unary call
resp, err := client.GetMidPrice(ctx, &pb.MidPriceRequest{Coin: "BTC"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("BTC mid: %s\n", resp.Price)
// Server-streaming
stream, err := client.StreamMids(ctx, &pb.StreamMidsRequest{
Coin: "BTC",
Subscription: "allMids",
})
if err != nil {
log.Fatal(err)
}
for {
update, err := stream.Recv()
if err != nil {
break
}
fmt.Printf("BTC: %s @ %d\n", update.Price, update.Timestamp)
}
}

Pre-decoded event feed via Server-Sent Events. The Unified Stream normalizes all Hyperliquid events into a consistent JSON schema, eliminating the need for client-side decoding.

Terminal window
# No auth required
curl https://unified.grpc.aleatoric.systems/healthz
# Response: {"status":"ok","uptime_seconds":123456}
Terminal window
# Stream all events
curl -N -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/stream"
# Filter by event type (comma-separated)
curl -N -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/stream?types=trade,liquidation_warning"
# Filter by coin
curl -N -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/stream?types=trade&coins=BTC,ETH"

Each SSE frame is delivered as a data: line containing a JSON object:

data: {"event_type":"trade","coin":"BTC","price":"98423.50","size":"0.15","side":"buy","ts":1741871234567}
data: {"event_type":"funding","coin":"BTC","rate_bps":"1.23","next_settlement":"2026-03-13T08:00:00Z","ts":1741871235000}

The Unified Stream also exposes REST endpoints for point-in-time queries:

Terminal window
# L2 order book snapshot
curl -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/l2-book?coin=BTC&depth=10"
# Consensus pulse (network health)
curl -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/consensus-pulse"
# Recent events (paginated)
curl -H "x-api-key: ak_live_YOUR_KEY_HERE" \
"https://unified.grpc.aleatoric.systems/api/v1/unified/events?type=trade&limit=50"
const API_KEY = 'ak_live_YOUR_KEY_HERE';
const BASE_URL = 'https://unified.grpc.aleatoric.systems';
// Note: native EventSource does not support custom headers.
// Use the eventsource package (npm install eventsource) for header support.
import EventSource from 'eventsource';
const es = new EventSource(
`${BASE_URL}/api/v1/unified/stream?types=trade,liquidation_warning`,
{ headers: { 'x-api-key': API_KEY } }
);
es.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(`[${data.event_type}] ${data.coin}: ${JSON.stringify(data)}`);
};
es.onerror = (err) => {
console.error('SSE error:', err);
// EventSource reconnects automatically
};

Raw Hyperliquid replica command data over WebSocket. This is the lowest-level transport, delivering the exact command stream from the Hyperliquid validator set. Enterprise tier only.

const API_KEY = 'ak_live_YOUR_KEY_HERE';
// Query parameter auth (browser-compatible)
const ws = new WebSocket(
`wss://disk.grpc.aleatoric.systems?api_key=${API_KEY}`
);
ws.onopen = () => {
console.log('Connected to Disk-Sync replica stream');
};
ws.onmessage = (msg) => {
const data = JSON.parse(msg.data);
// data.type: 'replica_cmd' | 'heartbeat'
// data.raw_hex: hex-encoded Hyperliquid command bytes
// data.sequence: monotonically increasing sequence number
// data.ts: server-side timestamp (nanoseconds)
if (data.type === 'replica_cmd') {
console.log(`Seq ${data.sequence}: ${data.raw_hex.slice(0, 40)}...`);
}
};
ws.onerror = (err) => console.error('WebSocket error:', err);
ws.onclose = (event) => {
console.log(`Disconnected: code=${event.code} reason=${event.reason}`);
// Implement reconnection with backoff
};
import asyncio
import json
import websockets
API_KEY = "ak_live_YOUR_KEY_HERE"
async def disk_sync():
uri = "wss://disk.grpc.aleatoric.systems"
headers = {"x-api-key": API_KEY}
async with websockets.connect(uri, extra_headers=headers) as ws:
print("Connected to Disk-Sync")
async for message in ws:
data = json.loads(message)
if data["type"] == "replica_cmd":
seq = data["sequence"]
raw = data["raw_hex"]
print(f"[{seq}] {raw[:40]}...")
elif data["type"] == "heartbeat":
print(f"Heartbeat @ {data['ts']}")
asyncio.run(disk_sync())

Note — Disk-Sync delivers raw, unprocessed Hyperliquid commands. Your application is responsible for decoding and interpreting the command bytes. For most use cases, the Unified Stream or gRPC transports provide a better developer experience with pre-decoded data.

The right transport depends on your latency requirements, message format preferences, and tier:

TransportLatencyMessage FormatStreamingMin. Tier
gRPC<10ms< 10\text{ms} p50Protobuf (binary)Server-streaming, bidirectionalPro
Unified Stream (SSE)<500μs< 500\mu\text{s} event deliveryJSON over SSEServer-pushPro
JSON-RPC<50ms< 50\text{ms} p50JSON over HTTPSRequest-response onlyBasic
Disk-Sync WebSocketRaw replica latencyHex-encoded commandsFull-duplexEnterprise

For trading systems where latency is critical, the expected round-trip time for a gRPC unary call is:

RTTgRPCtnetwork+tauth+tlookup2ms+1ms+5ms8ms\text{RTT}_{\text{gRPC}} \approx t_{\text{network}} + t_{\text{auth}} + t_{\text{lookup}} \approx 2\text{ms} + 1\text{ms} + 5\text{ms} \approx 8\text{ms}

For streaming RPCs, the first message incurs the connection setup cost, but subsequent messages arrive with only tnetworkt_{\text{network}} latency since authentication and connection state are cached.

Both US and Japan endpoints are available for all transports. Set the region by changing the endpoint hostname:

TransportUS EndpointJapan Endpoint
JSON-RPCrpc.aleatoric.systemsrpc-jp.aleatoric.systems
gRPChl.grpc.aleatoric.systems:443jp.grpc.aleatoric.systems:443
Unified Streamunified.grpc.aleatoric.systemsunified-jp.grpc.aleatoric.systems
Disk-Sync WebSocketdisk.grpc.aleatoric.systemsdisk-jp.grpc.aleatoric.systems

In the SDKs, switch to the Japan region via environment variables:

Terminal window
export HYPER_RPC_URL=https://rpc-jp.aleatoric.systems/
export HYPER_GRPC_TARGET=jp.grpc.aleatoric.systems:443
export HYPER_UNIFIED_STREAM_URL=https://unified-jp.grpc.aleatoric.systems

Or configure programmatically:

from hypercore_sdk import HyperCoreAPI
# Japan region
api = HyperCoreAPI(
rpc_url="https://rpc-jp.aleatoric.systems",
grpc_target="jp.grpc.aleatoric.systems:443",
)
import { HyperCoreAPI } from 'hypercore-ts-sdk';
const api = new HyperCoreAPI({
rpcUrl: 'https://rpc-jp.aleatoric.systems',
grpcTarget: 'jp.grpc.aleatoric.systems:443',
});
  1. Reuse connections — gRPC and WebSocket transports maintain persistent connections. Creating a new connection per request adds unnecessary latency and auth overhead.

  2. Handle disconnections — All streaming transports can disconnect due to server deployments, network issues, or idle timeouts. Implement reconnection with exponential backoff.

  3. Use the right transport — JSON-RPC for simple queries, gRPC for low-latency streaming, Unified Stream for pre-decoded event feeds, WebSocket for raw replica data.

  4. Set timeouts — For JSON-RPC, set a reasonable HTTP timeout (e.g., 10s). For gRPC, use deadline or timeout options. Hanging connections consume rate limit tokens.

  5. Monitor rate limits — Check X-RateLimit-Remaining and X-RateLimit-Reset response headers to proactively back off before hitting the limit.