TypeScript client for 0xArchive market data in Node services, dashboards, coding-agent workflows, and agent backends.
0xArchive is granular market data infrastructure for Hyperliquid and Lighter.xyz. Hyperliquid includes core perps (/v1/hyperliquid), HIP-3 builder perps (/v1/hyperliquid/hip3), HIP-4 outcome markets (/v1/hyperliquid/hip4), and Hyperliquid Spot (/v1/hyperliquid/spot). Lighter.xyz is the second top-level venue API at /v1/lighter. In this SDK these map to client.hyperliquid, client.hyperliquid.hip3, client.hyperliquid.hip4, client.spot, and client.lighter.
Use this SDK when the integration belongs in TypeScript or JavaScript code and you want typed REST helpers, WebSocket support, replay workflows, and order-book reconstruction utilities.
npm install @0xarchive/sdk
# or
yarn add @0xarchive/sdk
# or
pnpm add @0xarchive/sdkimport { OxArchive } from '@0xarchive/sdk';
const client = new OxArchive({ apiKey: '0xa_your_api_key' });
// First successful call: Hyperliquid BTC order book
const hlOrderbook = await client.hyperliquid.orderbook.get('BTC');
console.log(`Hyperliquid BTC mid price: ${hlOrderbook.midPrice}`);
// Lighter.xyz uses its own venue client
const lighterOrderbook = await client.lighter.orderbook.get('BTC');
console.log(`Lighter BTC mid price: ${lighterOrderbook.midPrice}`);
// Hyperliquid HIP-3 builder perps stay under client.hyperliquid.hip3
const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
const hip3Orderbook = await client.hyperliquid.hip3.orderbook.get('km:US500');
const hip3Trades = await client.hyperliquid.hip3.trades.recent('km:US500');
const hip3Funding = await client.hyperliquid.hip3.funding.current('xyz:XYZ100');
const hip3Oi = await client.hyperliquid.hip3.openInterest.current('xyz:XYZ100');
// Hyperliquid Spot lives at client.spot. Symbols are dashed canonical (HYPE-USDC).
const spotPairs = await client.spot.pairs.list();
const spotOrderbook = await client.spot.orderbook.get('HYPE-USDC');
const spotTrades = await client.spot.trades.recent('HYPE-USDC');
const spotCandles = await client.spot.candles.history('HYPE-USDC', {
start: Date.parse('2025-03-22T10:50:22Z'),
end: Date.now(),
interval: '1h',
});
// Get historical order book snapshots
const history = await client.hyperliquid.orderbook.history('ETH', {
start: Date.now() - 86400000, // 24 hours ago
end: Date.now(),
limit: 100
});| Need | Link |
|---|---|
| First authenticated route | Quick Start |
| SDK install and route docs | SDK docs |
| Claude Code, ChatGPT Codex, and coding-agent workflows | AI Clients |
| Example notebooks | Examples |
| File-based historical pulls | Data Catalog |
| Route contract and machine context | OpenAPI, llms.txt |
| Venue | Coverage | Notes |
|---|---|---|
| Hyperliquid | April 2023+ | Core perpetual markets; coverage varies by schema and route. |
| Hyperliquid HIP-3 | February 2026+ for served history | Builder perps with family-specific schema coverage; funding and trade history begin in February 2026. Candle history accepts up to 10,000 rows per request. |
| Hyperliquid HIP-4 | May 2026+ | Outcome markets. Candles and outcome-side open interest are served from 2026-05-02; OI updates at ~10s. No funding. |
| Hyperliquid Spot | Candles from 2025-03-22T10:50:22Z; trades from March 2025; orderbook, L4, TWAP from May 2026 | 326 authenticated inventory rows using dashed symbols (HYPE-USDC, PURR-USDC). Candle intervals are 1m/5m/15m/30m/1h/4h/1d/1w with max limit 1000 and numeric-string cursors passed through unchanged. No funding, OI, or liquidations. |
| Lighter.xyz | Candles served from 2025-08-01; observed global fill floor January 17, 2025; exact starts vary by market. L3 orderbooks from March 5, 2026+ | Perpetuals. Fills carry maker/taker context where served; L3 is capped at 250 orders per side and funding/OI update at approximately 10s. |
const client = new OxArchive({
apiKey: '0xa_your_api_key', // Required
baseUrl: 'https://api.0xarchive.io', // Optional
timeout: 30000, // Optional, request timeout in ms (default: 30000)
validate: false, // Optional, enable Zod schema validation
});Core resources (orderbook, trades, instruments, funding, openInterest, candles, freshness, summary, priceHistory) are exposed on the venue and family clients where the route is supported. Hyperliquid core, HIP-3, HIP-4, Spot, and Lighter have different resource subsets; see each section for details.
// Get current order book (Hyperliquid)
const orderbook = await client.hyperliquid.orderbook.get('BTC');
// Get current order book (Lighter.xyz)
const lighterOb = await client.lighter.orderbook.get('BTC');
// Get order book at specific timestamp with custom depth
const historical = await client.hyperliquid.orderbook.get('BTC', {
timestamp: 1704067200000,
depth: 20 // Number of levels per side
});
// Get historical snapshots (start is required)
const history = await client.hyperliquid.orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});The depth parameter controls how many price levels are returned per side. Depth limits are route-specific.
Note: Hyperliquid native L2 source data contains ~20 levels. Dedicated L2 routes derived from L4 can return all available levels where supported. Lighter L3 is individual order-level data capped at 250 orders per side, not an unlimited full-depth book. Depth limits apply to L2 snapshot endpoints only — L4 and L2 diff endpoints return full data.
Lighter.xyz orderbook history supports a granularity parameter for different data resolutions.
| Granularity | Interval | Credit Multiplier |
|---|---|---|
checkpoint |
~60s | 1x |
30s |
30s | 2x |
10s |
10s | 3x |
1s |
1s | 10x |
tick |
tick-level | 20x |
// Get Lighter orderbook history with 10s resolution
const history = await client.lighter.orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
granularity: '10s'
});
// Get 1-second resolution
const history = await client.lighter.orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
granularity: '1s'
});
// Tick-level data - returns checkpoint + raw deltas
const history = await client.lighter.orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
granularity: 'tick'
});Note: The granularity parameter is ignored for Hyperliquid orderbook history.
For tick-level data, the SDK provides client-side orderbook reconstruction. This efficiently reconstructs full orderbook state from a checkpoint and incremental deltas.
import { OrderBookReconstructor } from '@0xarchive/sdk';
// Option 1: Get fully reconstructed snapshots (simplest)
const snapshots = await client.lighter.orderbook.historyReconstructed('BTC', {
start: Date.now() - 3600000,
end: Date.now()
});
for (const ob of snapshots) {
console.log(`${ob.timestamp}: bid=${ob.bids[0]?.px} ask=${ob.asks[0]?.px}`);
}
// Option 2: Get raw tick data for custom reconstruction
const tickData = await client.lighter.orderbook.historyTick('BTC', {
start: Date.now() - 3600000,
end: Date.now()
});
console.log(`Checkpoint: ${tickData.checkpoint.bids.length} bids`);
console.log(`Deltas: ${tickData.deltas.length} updates`);
// Option 3: Auto-paginating iterator (recommended for large time ranges)
// Automatically handles pagination, fetching up to 1,000 deltas per request
for await (const snapshot of client.lighter.orderbook.iterateTickHistory('BTC', {
start: Date.now() - 86400000, // 24 hours of data
end: Date.now()
})) {
console.log(snapshot.timestamp, 'Mid:', snapshot.midPrice);
if (someCondition(snapshot)) break; // Early exit supported
}
// Option 4: Manual iteration (single page, for custom logic)
const reconstructor = client.lighter.orderbook.createReconstructor();
for (const snapshot of reconstructor.iterate(tickData.checkpoint, tickData.deltas)) {
// Process each snapshot without loading all into memory
if (someCondition(snapshot)) break; // Early exit if needed
}
// Option 5: Get only final state (most efficient)
const final = reconstructor.reconstructFinal(tickData.checkpoint, tickData.deltas);
// Check for sequence gaps
const gaps = OrderBookReconstructor.detectGaps(tickData.deltas);
if (gaps.length > 0) {
console.warn('Sequence gaps detected:', gaps);
}Methods:
| Method | Description |
|---|---|
historyTick(coin, params) |
Get raw checkpoint + deltas (single page, max 1,000 deltas) |
historyReconstructed(coin, params, options) |
Get fully reconstructed snapshots (single page) |
iterateTickHistory(coin, params, depth?) |
Auto-paginating async iterator for large time ranges |
createReconstructor() |
Create a reconstructor instance for manual control |
Note: The API returns a maximum of 1,000 deltas per request. For time ranges with more deltas, use iterateTickHistory() which handles pagination automatically.
ReconstructOptions:
| Option | Default | Description |
|---|---|---|
depth |
all | Maximum price levels in output |
emitAll |
true |
If false, only return final state |
The trades API uses cursor-based pagination for efficient retrieval of large datasets.
// Get trade history with cursor-based pagination
let result = await client.hyperliquid.trades.list('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});
// Paginate through all results
const allTrades = [...result.data];
while (result.nextCursor) {
result = await client.hyperliquid.trades.list('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
cursor: result.nextCursor,
limit: 1000
});
allTrades.push(...result.data);
}
// Get recent trades (Lighter only - has real-time data)
const recent = await client.lighter.trades.recent('BTC', 100);Note: The recent() method is available for Lighter.xyz (client.lighter.trades.recent()), HIP-3 (client.hyperliquid.hip3.trades.recent()), and HIP-4 (client.hyperliquid.hip4.trades.recent() / getTradesRecent()) -- all three have real-time ingestion. Hyperliquid does not have a recent trades endpoint (it uses hourly S3 backfill); calling client.hyperliquid.trades.recent() throws a structured OxArchiveError directing you to use list() with a time range instead.
Lighter trades are fill-grain: describe the returned records as per fill · maker + taker where that context is served. Do not describe them as every order, a complete order history, or an unlimited trade archive.
// List all trading instruments (Hyperliquid)
const instruments = await client.hyperliquid.instruments.list();
// Get specific instrument details
const btc = await client.hyperliquid.instruments.get('BTC');
console.log(`BTC size decimals: ${btc.szDecimals}`);Lighter instruments have a different schema with additional fields for fees, market IDs, and minimum order amounts:
// List Lighter instruments (returns LighterInstrument, not Instrument)
const lighterInstruments = await client.lighter.instruments.list();
// Get specific Lighter instrument
const eth = await client.lighter.instruments.get('ETH');
console.log(`ETH taker fee: ${eth.takerFee}`);
console.log(`ETH maker fee: ${eth.makerFee}`);
console.log(`ETH market ID: ${eth.marketId}`);
console.log(`ETH min base amount: ${eth.minBaseAmount}`);Key differences:
| Field | Hyperliquid (Instrument) |
Lighter (LighterInstrument) |
|---|---|---|
| Symbol | name |
symbol |
| Size decimals | szDecimals |
sizeDecimals |
| Fee info | Not available | takerFee, makerFee, liquidationFee |
| Market ID | Not available | marketId |
| Min amounts | Not available | minBaseAmount, minQuoteAmount |
HIP-3 instruments are derived from live market data and include mark price, open interest, and mid price:
// List all HIP-3 instruments
const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
for (const inst of hip3Instruments) {
console.log(`${inst.coin} (${inst.namespace}:${inst.ticker}): mark=${inst.markPrice}, OI=${inst.openInterest}`);
}
// Get specific HIP-3 instrument (case-sensitive)
const us500 = await client.hyperliquid.hip3.instruments.get('km:US500');
console.log(`Mark price: ${us500.markPrice}`);Available HIP-3 Coins:
| Builder | Coins |
|---|---|
| xyz (Hyperliquid) | xyz:XYZ100 |
| km (Kinetiq Markets) | km:US500, km:SMALL2000, km:GOOGL, km:USBOND, km:GOLD, km:USTECH, km:NVDA, km:SILVER, km:BABA |
HIP-3 breadth reports the percentage of eligible instruments trading above
their current UTC-session VWAP. History begins on 2026-08-28 and is
aggregate-only; there is no synthetic pre-launch history. Instruments with no
session volume or a stale last completed candle are excluded, so coverage can
change overnight and on weekends. Percentages should not be averaged across
snapshots; interval uses the last snapshot in each bucket.
const breadthNow = await client.hyperliquid.hip3.breadth.current();
const breadthHistory = await client.hyperliquid.hip3.breadth.history({
start: Date.parse('2026-08-28T00:00:00Z'),
end: Date.now(),
interval: '5m',
});
// valuePct is null when no instruments are eligible, not 0.
console.log(breadthNow.valuePct, breadthHistory.nextCursor);HIP-4 is Hyperliquid's binary outcome-market namespace. Each outcome has 2 sides (#0 = Yes / side 0, #1 = No / side 1, etc.). Markets are fully collateralized so there are no funding rates or liquidations. Candle history and outcome-side open interest are served from 2026-05-02; OI updates at ~10s. mark_price, midPrice, and candle OHLC values are implied probabilities in [0, 1], not USD prices.
Path encoding: the backend accepts both the bare numeric form ('0', '1', ...) and the legacy on-chain #-prefixed form ('#0', '#1', ...). The bare numeric form is the documented primary path. The SDK URL-encodes the legacy value on the wire (# becomes %23) so it survives fetch parsing; both forms remain accepted.
// Per-side instruments (one row per #N coin)
const sides = await client.hyperliquid.hip4.instruments.list();
for (const s of sides) {
console.log(`${s.symbol} (outcome ${s.outcomeId}/${s.side}): ${s.displayTitle}`);
console.log(` slug: ${s.slug}, settled: ${s.isSettled}`);
}
// Per-outcome aggregates (one row per outcome)
const outcomes = await client.hyperliquid.hip4.listOutcomes({ isSettled: false, limit: 50 });
for (const o of outcomes.data) {
console.log(`outcome ${o.outcomeId}: ${o.displayTitle}`);
console.log(` pair: ${o.outcomePair?.[0]} / ${o.outcomePair?.[1]}`);
console.log(` expiry: ${o.expiry}, target: ${o.targetPrice}`);
}
// Detail (includes aggregatedOi)
const detail = await client.hyperliquid.hip4.getOutcome(0);
console.log(detail.aggregatedOi?.outcomeDisplayOpenInterestContracts);
// Slug-based lookup (per-outcome OR per-side slug)
const bySlug = await client.hyperliquid.hip4.getOutcomeBySlug('btc-above-78213-may-04-0600');
// Slug-filter on listOutcomes
const filtered = await client.hyperliquid.hip4.listOutcomes({
slug: 'btc-above-78213-may-04-0600',
});
// Orderbook. Bare numeric form is recommended.
const ob = await client.hyperliquid.hip4.getOrderbook('0');
// ob.midPrice is a probability ∈ [0, 1] — implied YES probability for #0.
// Trades, OI, and prices
const trades = await client.hyperliquid.hip4.getTradesRecent('0', 50);
const oiNow = await client.hyperliquid.hip4.getOpenInterestCurrent('0');
const prices = await client.hyperliquid.hip4.getPrices('0', {
start: Date.now() - 86400000,
end: Date.now(),
});
// Candle history (served from 2026-05-02; OHLC values are probabilities)
const candles = await client.hyperliquid.hip4.candles.history('0', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1m',
});
// Convenience
const summary = await client.hyperliquid.hip4.getSummary('0');
const fresh = await client.hyperliquid.hip4.getFreshness('0');
// L4
const l4 = await client.hyperliquid.hip4.getL4Orderbook('0');
const orders = await client.hyperliquid.hip4.getOrderHistory('0', {
start: Date.now() - 3600000,
end: Date.now(),
});No HIP-4 funding or liquidations. HIP-4 candles and outcome-side open interest are available through the routes documented above; the SDK intentionally does not expose a HIP-4 funding resource.
Spot pairs live at /v1/hyperliquid/spot and client.spot. Symbols are dashed canonical (HYPE-USDC, PURR-USDC); the server resolves the dashed form to Hyperliquid's wire formats (PURR/USDC, @107) internally.
Spot has no funding, open interest, or liquidations. Candle history is served
through GET /v1/hyperliquid/spot/candles/{symbol} and
client.spot.candles.history(), with coverage from 2025-03-22T10:50:22Z.
The candle route accepts 1m, 5m, 15m, 30m, 1h, 4h, 1d, and 1w,
with a maximum limit of 1000 and numeric-string pagination cursors that
callers should pass through unchanged.
// Pairs (one row per dashed symbol)
const pairs = await client.spot.pairs.list();
const hype = await client.spot.pairs.get('HYPE-USDC');
console.log(`${hype.symbol}: mark=${hype.markPrice}, mid=${hype.midPrice}`);
// Orderbook (live from 2026-05-05)
const ob = await client.spot.orderbook.get('HYPE-USDC');
console.log(`${ob.coin} mid: ${ob.midPrice}`);
// Orderbook history
const obHistory = await client.spot.orderbook.history('HYPE-USDC', {
start: Date.now() - 3600000,
end: Date.now(),
limit: 100,
});
// Trade history (S3 backfill from 2025-03-22)
const trades = await client.spot.trades.list('HYPE-USDC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000,
});
// Recent trades (real-time)
const recent = await client.spot.trades.recent('HYPE-USDC', 100);
// Candle history (served from 2025-03-22T10:50:22Z)
const candles = await client.spot.candles.history('HYPE-USDC', {
start: Date.parse('2025-03-22T10:50:22Z'),
end: Date.now(),
interval: '1h',
limit: 1000,
});
// L4 reconstruction (live from 2026-05-05)
const l4 = await client.spot.l4Orderbook.get('HYPE-USDC');
const diffs = await client.spot.l4Orderbook.diffs('HYPE-USDC', {
start: Date.now() - 3600000,
end: Date.now(),
});
// Order lifecycle events
const orders = await client.spot.orders.history('HYPE-USDC', {
start: Date.now() - 86400000,
end: Date.now(),
});
// TWAP statuses by symbol or by user wallet
const bySymbol = await client.spot.twap.bySymbol('HYPE-USDC', {
start: Date.now() - 86400000,
end: Date.now(),
});
const byUser = await client.spot.twap.byUser('0xabc...', {
start: Date.now() - 86400000,
end: Date.now(),
});
// Per-symbol freshness across all spot data types
const fresh = await client.spot.freshness('HYPE-USDC');
console.log(`Orderbook last updated: ${fresh.orderbook.lastUpdated}`);Coverage caveats. Spot candle history is served from 2025-03-22T10:50:22Z through
GET /v1/hyperliquid/spot/candles/{symbol}. The route supports 1m, 5m, 15m, 30m, 1h, 4h, 1d, and 1w intervals, up to 1000 rows per request; numeric-string cursors should be passed back unchanged. Spot trades go back to 2025-03-22 (the earliest date Hyperliquid published S3 spot fills). Pre-March 2025 spot history is unrecoverable from any free public archive. Spot orderbook, L4, and TWAP data are live-only from 2026-05-05; Hyperliquid does not publish historical spot orderbook data.
For Lighter, funding_rate and the SDK's fundingRate are fractional and
non-annualized. Consumers that compensated for the former percent units must
update their conversion.
// Get current funding rate
const current = await client.hyperliquid.funding.current('BTC');
// Get funding rate history (start is required)
const history = await client.hyperliquid.funding.history('ETH', {
start: Date.now() - 86400000 * 7,
end: Date.now()
});
// Get funding rate history with aggregation interval
const hourly = await client.hyperliquid.funding.history('BTC', {
start: Date.now() - 86400000 * 7,
end: Date.now(),
interval: '1h'
});| Parameter | Type | Required | Description |
|---|---|---|---|
start |
number | string |
Yes | Start timestamp (Unix ms or ISO string) |
end |
number | string |
Yes | End timestamp (Unix ms or ISO string) |
cursor |
number | string |
No | Cursor from previous response for pagination |
limit |
number |
No | Max results (default: 100, max: 1000) |
interval |
OiFundingInterval |
No | Aggregation interval: '5m', '15m', '30m', '1h', '4h', '1d'. When omitted, the route's native/default cadence is returned. |
Funding cadence is family-specific: Hyperliquid core is approximately 1 minute, HIP-3 is approximately 10 seconds, and Lighter is approximately 10 seconds. HIP-4 does not expose a funding route.
// Get current open interest
const current = await client.hyperliquid.openInterest.current('BTC');
// Get open interest history (start is required)
const history = await client.hyperliquid.openInterest.history('ETH', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 100
});
// Get open interest history with aggregation interval
const hourly = await client.hyperliquid.openInterest.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h'
});| Parameter | Type | Required | Description |
|---|---|---|---|
start |
number | string |
Yes | Start timestamp (Unix ms or ISO string) |
end |
number | string |
Yes | End timestamp (Unix ms or ISO string) |
cursor |
number | string |
No | Cursor from previous response for pagination |
limit |
number |
No | Max results (default: 100, max: 1000) |
interval |
OiFundingInterval |
No | Aggregation interval: '5m', '15m', '30m', '1h', '4h', '1d'. When omitted, the route's native/default cadence is returned. |
Open-interest cadence is family-specific. HIP-4 outcome-side OI is served from 2026-05-02 at ~10s, and Lighter OI updates at approximately 10 seconds. Do not infer a universal one-minute cadence from this shared type.
Get historical liquidation events. Data available from May 2025 onwards for Hyperliquid, and from February 2026 for HIP-3.
// Get liquidation history for a coin (Hyperliquid)
const liquidations = await client.hyperliquid.liquidations.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 100
});
// Paginate through all results
const allLiquidations = [...liquidations.data];
while (liquidations.nextCursor) {
const next = await client.hyperliquid.liquidations.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
cursor: liquidations.nextCursor,
limit: 1000
});
allLiquidations.push(...next.data);
}
// Get liquidations for a specific user
const userLiquidations = await client.hyperliquid.liquidations.byUser('0x1234...', {
start: Date.now() - 86400000 * 7,
end: Date.now(),
coin: 'BTC' // optional filter
});
// HIP-3 liquidations (case-sensitive coins)
const hip3Liquidations = await client.hyperliquid.hip3.liquidations.history('km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 100
});Get pre-aggregated liquidation volume in time-bucketed intervals. Returns total, long, and short USD volumes per bucket -- 100-1000x less data than individual liquidation records.
// Get hourly liquidation volume for the last week (Hyperliquid)
const volume = await client.hyperliquid.liquidations.volume('BTC', {
start: Date.now() - 86400000 * 7,
end: Date.now(),
interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
});
for (const bucket of volume.data) {
console.log(`${bucket.timestamp}: total=$${bucket.totalUsd}, long=$${bucket.longUsd}, short=$${bucket.shortUsd}`);
}
// HIP-3 liquidation volume (case-sensitive coins)
const hip3Volume = await client.hyperliquid.hip3.liquidations.volume('km:US500', {
start: Date.now() - 86400000 * 7,
end: Date.now(),
interval: '1h'
});Access order history, order flow aggregations, and TP/SL (take-profit/stop-loss) orders. Available for Hyperliquid and HIP-3.
// Get order history for a coin
const orders = await client.hyperliquid.orders.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000,
user: '0x1234...', // optional: filter by user address
status: 'filled', // optional: filter by status
order_type: 'limit', // optional: filter by order type
});
// Paginate through all results
const allOrders = [...orders.data];
while (orders.nextCursor) {
const next = await client.hyperliquid.orders.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
cursor: orders.nextCursor,
limit: 1000
});
allOrders.push(...next.data);
}
// Get order flow (aggregated order activity over time)
const flow = await client.hyperliquid.orders.flow('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h', // optional aggregation interval
limit: 100
});
// Get TP/SL orders
const tpsl = await client.hyperliquid.orders.tpsl('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
user: '0x1234...', // optional: filter by user
triggered: true, // optional: filter by triggered status
});
// HIP-3 orders (case-sensitive coins)
const hip3Orders = await client.hyperliquid.hip3.orders.history('km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});
const hip3Flow = await client.hyperliquid.hip3.orders.flow('km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h'
});
const hip3Tpsl = await client.hyperliquid.hip3.orders.tpsl('km:US500', {
start: Date.now() - 86400000,
end: Date.now()
});Access L4 orderbook snapshots, diffs, and history. L4 data includes user attribution (who placed each order). Available for Hyperliquid and HIP-3.
// Get current L4 orderbook snapshot
const l4Ob = await client.hyperliquid.l4Orderbook.get('BTC');
// Get L4 orderbook at a specific timestamp with custom depth
const l4Historical = await client.hyperliquid.l4Orderbook.get('BTC', {
timestamp: 1704067200000,
depth: 20
});
// Get L4 orderbook diffs (incremental updates)
const diffs = await client.hyperliquid.l4Orderbook.diffs('BTC', {
start: Date.now() - 3600000,
end: Date.now(),
limit: 1000
});
// Paginate through diffs
const allDiffs = [...diffs.data];
while (diffs.nextCursor) {
const next = await client.hyperliquid.l4Orderbook.diffs('BTC', {
start: Date.now() - 3600000,
end: Date.now(),
cursor: diffs.nextCursor,
limit: 1000
});
allDiffs.push(...next.data);
}
// Get L4 orderbook history (full snapshots over time)
const l4History = await client.hyperliquid.l4Orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});
// HIP-3 L4 orderbook (case-sensitive coins)
const hip3L4 = await client.hyperliquid.hip3.l4Orderbook.get('km:US500');
const hip3L4Diffs = await client.hyperliquid.hip3.l4Orderbook.diffs('km:US500', {
start: Date.now() - 3600000,
end: Date.now(),
limit: 1000
});
const hip3L4History = await client.hyperliquid.hip3.l4Orderbook.history('km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});Access L3 orderbook snapshots and history from Lighter.xyz. L3 data contains individual resting orders and is capped at 250 orders per side. It is not an unlimited all-orders book.
// Get current L3 orderbook
const l3Ob = await client.lighter.l3Orderbook.get('BTC');
// Get L3 orderbook at a specific timestamp with custom depth
const l3Historical = await client.lighter.l3Orderbook.get('BTC', {
timestamp: '2026-03-05T00:00:00Z',
depth: 250
});
// Get L3 orderbook history
const l3History = await client.lighter.l3Orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});
// Paginate through L3 history
const allL3 = [...l3History.data];
while (l3History.nextCursor) {
const next = await client.lighter.l3Orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
cursor: l3History.nextCursor,
limit: 1000
});
allL3.push(...next.data);
}Access L2 full-depth orderbook derived from L4 data. Available for Hyperliquid and HIP-3.
// L2 full-depth orderbook
const l2 = await client.hyperliquid.l2Orderbook.get('BTC');
// L2 orderbook at a specific timestamp with depth
const l2Historical = await client.hyperliquid.l2Orderbook.get('BTC', {
timestamp: 1704067200000,
depth: 50
});
// L2 orderbook history
const l2History = await client.hyperliquid.l2Orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
limit: 1000
});
// L2 tick-level diffs
const l2Diffs = await client.hyperliquid.l2Orderbook.diffs('BTC', {
start: Date.now() - 3600000,
end: Date.now(),
limit: 1000
});
// HIP-3 L2 orderbook
const hip3L2 = await client.hyperliquid.hip3.l2Orderbook.get('km:US500');Check when each data type was last updated for a specific coin. Useful for verifying data recency before pulling it. Available across venue APIs.
// Hyperliquid
const freshness = await client.hyperliquid.freshness('BTC');
console.log(`Orderbook last updated: ${freshness.orderbook.lastUpdated}, lag: ${freshness.orderbook.lagMs}ms`);
console.log(`Trades last updated: ${freshness.trades.lastUpdated}, lag: ${freshness.trades.lagMs}ms`);
console.log(`Funding last updated: ${freshness.funding.lastUpdated}`);
console.log(`OI last updated: ${freshness.openInterest.lastUpdated}`);
// Lighter.xyz
const lighterFreshness = await client.lighter.freshness('BTC');
// HIP-3 (case-sensitive coins)
const hip3Freshness = await client.hyperliquid.hip3.freshness('km:US500');Get a combined market snapshot in a single call -- mark/oracle price, funding rate, open interest, 24h volume, and 24h liquidation volumes.
// Hyperliquid (includes volume + liquidation data)
const summary = await client.hyperliquid.summary('BTC');
console.log(`Mark price: ${summary.markPrice}`);
console.log(`Oracle price: ${summary.oraclePrice}`);
console.log(`Funding rate: ${summary.fundingRate}`);
console.log(`Open interest: ${summary.openInterest}`);
console.log(`24h volume: ${summary.volume24h}`);
console.log(`24h liquidation volume: $${summary.liquidationVolume24h}`);
console.log(` Long: $${summary.longLiquidationVolume24h}`);
console.log(` Short: $${summary.shortLiquidationVolume24h}`);
// Lighter.xyz (price, funding, OI — no volume/liquidation data)
const lighterSummary = await client.lighter.summary('BTC');
// HIP-3 (includes mid_price — case-sensitive coins)
const hip3Summary = await client.hyperliquid.hip3.summary('km:US500');
console.log(`Mid price: ${hip3Summary.midPrice}`);Get mark, oracle, and mid price history over time. Supports aggregation intervals. Data projected from open interest records.
// Hyperliquid: available from April 2023
const prices = await client.hyperliquid.priceHistory('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
});
for (const snapshot of prices.data) {
console.log(`${snapshot.timestamp}: mark=${snapshot.markPrice}, oracle=${snapshot.oraclePrice}, mid=${snapshot.midPrice}`);
}
// Lighter.xyz
const lighterPrices = await client.lighter.priceHistory('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h'
});
// HIP-3 (case-sensitive coins)
const hip3Prices = await client.hyperliquid.hip3.priceHistory('km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1d'
});
// Paginate for larger ranges
let result = await client.hyperliquid.priceHistory('BTC', {
start: Date.now() - 86400000 * 30,
end: Date.now(),
interval: '4h',
limit: 1000
});
while (result.nextCursor) {
result = await client.hyperliquid.priceHistory('BTC', {
start: Date.now() - 86400000 * 30,
end: Date.now(),
interval: '4h',
cursor: result.nextCursor,
limit: 1000
});
}Get historical OHLCV candle data aggregated from trades.
// Get candle history (start is required)
const candles = await client.hyperliquid.candles.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1h', // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
limit: 100
});
// Iterate through candles
for (const candle of candles.data) {
console.log(`${candle.timestamp}: O=${candle.open} H=${candle.high} L=${candle.low} C=${candle.close} V=${candle.volume}`);
}
// Cursor-based pagination for large datasets
let result = await client.hyperliquid.candles.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1m',
limit: 1000
});
const allCandles = [...result.data];
while (result.nextCursor) {
result = await client.hyperliquid.candles.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '1m',
cursor: result.nextCursor,
limit: 1000
});
allCandles.push(...result.data);
}
// Lighter.xyz candles
const lighterCandles = await client.lighter.candles.history('BTC', {
start: Date.now() - 86400000,
end: Date.now(),
interval: '15m'
});
// Hyperliquid Spot candles (served from 2025-03-22T10:50:22Z)
const spotCandles = await client.spot.candles.history('HYPE-USDC', {
start: '2025-03-22T10:50:22Z',
end: new Date().toISOString(),
interval: '1h',
limit: 1000,
});| Interval | Description |
|---|---|
1m |
1 minute |
5m |
5 minutes |
15m |
15 minutes |
30m |
30 minutes |
1h |
1 hour (default) |
4h |
4 hours |
1d |
1 day |
1w |
1 week |
nextCursor values are returned as numeric strings. Treat each as an opaque
client value and pass it back unchanged as cursor when requesting the next
page. Core Hyperliquid, HIP-3, and Lighter candle routes accept up to 10,000
rows per request; HIP-4 and Spot routes accept up to 1,000. Hyperliquid Spot
candle history starts at 2025-03-22T10:50:22Z.
Monitor data coverage, incidents, latency, and SLA compliance across venue APIs.
// Get overall system health status
const status = await client.dataQuality.status();
console.log(`System status: ${status.status}`);
for (const [exchange, info] of Object.entries(status.exchanges)) {
console.log(` ${exchange}: ${info.status}`);
}
// Get data coverage summary for venue APIs
const coverage = await client.dataQuality.coverage();
for (const exchange of coverage.exchanges) {
console.log(`${exchange.exchange}:`);
for (const [dtype, info] of Object.entries(exchange.dataTypes)) {
console.log(` ${dtype}: ${info.totalRecords.toLocaleString()} records, ${info.completeness}% complete`);
}
}
// Get symbol-specific coverage with gap detection
const btc = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC');
const oi = btc.dataTypes.open_interest;
console.log(`BTC OI completeness: ${oi.completeness}%`);
console.log(`Historical coverage: ${oi.historicalCoverage}%`); // Hour-level granularity
console.log(`Gaps found: ${oi.gaps.length}`);
for (const gap of oi.gaps.slice(0, 5)) {
console.log(` ${gap.durationMinutes} min gap: ${gap.start} -> ${gap.end}`);
}
// Check empirical data cadence (when available)
const ob = btc.dataTypes.orderbook;
if (ob.cadence) {
console.log(`Orderbook cadence: ~${ob.cadence.medianIntervalSeconds}s median, p95=${ob.cadence.p95IntervalSeconds}s`);
}
// Time-bounded gap detection (last 7 days)
const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
const btc7d = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC', {
from: weekAgo,
to: Date.now(),
});
// List incidents with filtering
const result = await client.dataQuality.listIncidents({ status: 'open' });
for (const incident of result.incidents) {
console.log(`[${incident.severity}] ${incident.title}`);
}
// Get latency metrics
const latency = await client.dataQuality.latency();
for (const [exchange, metrics] of Object.entries(latency.exchanges)) {
console.log(`${exchange}: OB lag ${metrics.dataFreshness.orderbookLagMs}ms`);
}
// Get SLA compliance metrics for a specific month
const sla = await client.dataQuality.sla({ year: 2026, month: 1 });
console.log(`Period: ${sla.period}`);
console.log(`Uptime: ${sla.actual.uptime}% (${sla.actual.uptimeStatus})`);
console.log(`API P99: ${sla.actual.apiLatencyP99Ms}ms (${sla.actual.latencyStatus})`);| Method | Description |
|---|---|
status() |
Overall system health and per-exchange status |
coverage() |
Data coverage summary for venue APIs |
exchangeCoverage(exchange) |
Coverage details for a specific exchange |
symbolCoverage(exchange, symbol, options?) |
Coverage with gap detection, cadence, and historical coverage |
listIncidents(params) |
List incidents with filtering and pagination |
getIncident(incidentId) |
Get specific incident details |
latency() |
Current latency metrics (WebSocket, REST, data freshness) |
sla(params) |
SLA compliance metrics for a specific month |
Note: Data Quality endpoints (coverage(), exchangeCoverage(), symbolCoverage()) perform complex aggregation queries and may take 30-60 seconds on first request (results are cached server-side for 5 minutes). If you encounter timeout errors, create a client with a longer timeout:
const client = new OxArchive({
apiKey: '0xa_your_api_key',
timeout: 60000 // 60 seconds for data quality endpoints
});Get API keys programmatically using an Ethereum wallet. No browser or email required.
Free includes every market, route, schema, and served depth, with history limited to the most recent rolling 30 days and a maximum 30-day span per request or replay. Build and above keep the full retained archive. Plans gate capacity and Free's 30-day history window, not route families, schemas, or served depth. See Pricing for plan capacity.
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet } from 'viem/chains';
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const walletClient = createWalletClient({ account, chain: mainnet, transport: http() });
// 1. Get SIWE challenge
const challenge = await client.web3.challenge(account.address);
// 2. Sign with personal_sign (EIP-191)
const signature = await walletClient.signMessage({ message: challenge.message });
// 3. Submit → receive API key
const result = await client.web3.signup(challenge.message, signature);
console.log(result.apiKey); // "0xa_..."import { createWalletClient, http, encodePacked } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import crypto from 'crypto';
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const walletClient = createWalletClient({ account, chain: base, transport: http() });
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
// 1. Get pricing
const quote = await client.web3.subscribeQuote('build');
// quote.amount = "49000000" ($49 USDC), quote.payTo = "0x..."
// 2. Build & sign EIP-3009 transferWithAuthorization
const nonce = `0x${crypto.randomBytes(32).toString('hex')}` as `0x${string}`;
const validAfter = 0n;
const validBefore = BigInt(Math.floor(Date.now() / 1000) + 3600);
const signature = await walletClient.signTypedData({
domain: {
name: 'USD Coin',
version: '2',
chainId: 8453,
verifyingContract: USDC_ADDRESS,
},
types: {
TransferWithAuthorization: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' },
{ name: 'nonce', type: 'bytes32' },
],
},
primaryType: 'TransferWithAuthorization',
message: {
from: account.address,
to: quote.payTo as `0x${string}`,
value: BigInt(quote.amount),
validAfter,
validBefore,
nonce,
},
});
// 3. Build x402 payment envelope and base64-encode
const paymentPayload = btoa(JSON.stringify({
x402Version: 2,
payload: {
signature,
authorization: {
from: account.address,
to: quote.payTo,
value: quote.amount,
validAfter: '0',
validBefore: validBefore.toString(),
nonce,
},
},
}));
// 4. Submit payment → receive API key + subscription
const sub = await client.web3.subscribe('build', paymentPayload);
console.log(sub.apiKey, sub.tier, sub.expiresAt);// List and revoke keys (requires a fresh SIWE signature)
const keys = await client.web3.listKeys(challenge.message, signature);
await client.web3.revokeKey(challenge.message, signature, keys.keys[0].id);The following legacy methods are deprecated and will be removed in v2.0. They default to Hyperliquid data:
// Deprecated - use client.hyperliquid.orderbook.get() instead
const orderbook = await client.orderbook.get('BTC');
// Deprecated - use client.hyperliquid.trades.list() instead
const trades = await client.trades.list('BTC', { start, end });The WebSocket client supports live subscriptions for supported Hyperliquid and Lighter.xyz channels, and historical replay. For file-based historical exports, use the Data Catalog.
Bulk streaming over WebSocket has been discontinued.
ws.stream(),ws.multiStream(), andws.streamStop()remain for compatibility but are deprecated: the server answers them with an error message and sends no data. For large downloads, use the S3 Parquet bulk export from the Data Catalog.
Lighter
lighter_orderbook,lighter_trades,lighter_open_interest, andlighter_fundingsupport live subscriptions and replay.lighter_candlesandlighter_l3_orderbookare replay-only. See Live Lighter.xyz Channels.
import { OxArchiveWs } from '@0xarchive/sdk';
const ws = new OxArchiveWs({ apiKey: '0xa_your_api_key' });Subscribe to live market data from Hyperliquid. Live Lighter.xyz data uses the same client; see Live Lighter.xyz Channels.
ws.connect({
onOpen: () => console.log('Connected'),
onClose: (code, reason) => console.log(`Disconnected: ${code}`),
onError: (error) => console.error('Error:', error),
});
// Subscribe to channels
ws.subscribeOrderbook('BTC');
ws.subscribeTrades('ETH');
ws.subscribeTicker('SOL');
ws.subscribeAllTickers();
// Handle real-time data with typed callbacks
ws.onOrderbook((coin, data) => {
console.log(`${coin} mid price: ${data.midPrice}`);
});
ws.onTrades((coin, trades) => {
console.log(`${coin} new trades: ${trades.length}`);
});
// Unsubscribe when done
ws.unsubscribeOrderbook('BTC');
// Disconnect
ws.disconnect();Replay historical data with original timing preserved. Perfect for backtesting.
Important: Replay data is delivered via
onHistoricalData(), NOTonTrades()oronOrderbook(). The real-time callbacks only receive live market data from subscriptions.
const ws = new OxArchiveWs({ apiKey: 'ox_...' });
ws.connect();
// Handle replay data - this is where historical records arrive
ws.onHistoricalData((coin, timestamp, data) => {
console.log(`${new Date(timestamp).toISOString()}: ${data.midPrice}`);
});
// Replay lifecycle events
ws.onReplayStart((channel, coin, start, end, speed) => {
console.log(`Starting replay: ${channel}/${coin} at ${speed}x`);
});
ws.onReplayComplete((channel, coin, recordsSent) => {
console.log(`Replay complete: ${recordsSent} records`);
});
// Start replay at 10x speed
ws.replay('orderbook', 'BTC', {
start: Date.now() - 86400000, // 24 hours ago
end: Date.now(), // End of bounded replay window
speed: 10 // Optional, defaults to 1x
});
// Lighter.xyz replay with granularity. Replay keeps its existing row shapes,
// which differ from the live Lighter payloads.
ws.replay('lighter_orderbook', 'BTC', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 10,
granularity: '10s' // Options: 'checkpoint', '30s', '10s', '1s', 'tick'
});
// Handle tick-level data (granularity='tick')
ws.onHistoricalTickData((coin, checkpoint, deltas) => {
console.log(`Checkpoint: ${checkpoint.bids.length} bids`);
console.log(`Deltas: ${deltas.length} updates`);
// Apply deltas to checkpoint to reconstruct orderbook at any point
});
// Control playback
ws.replayPause();
ws.replayResume();
ws.replaySeek(1704067200000); // Jump to timestamp
ws.replayStop();During historical replay, the server automatically detects gaps in the data and notifies the client. This helps identify periods where data may be missing.
// Handle gap notifications during replay
ws.onGap((channel, coin, gapStart, gapEnd, durationMinutes) => {
console.log(`Gap detected in ${channel}/${coin}:`);
console.log(` From: ${new Date(gapStart).toISOString()}`);
console.log(` To: ${new Date(gapEnd).toISOString()}`);
console.log(` Duration: ${durationMinutes} minutes`);
});
// Start replay - gaps will be reported via onGap callback
ws.replay('orderbook', 'BTC', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 10
});Gap thresholds vary by channel:
- orderbook, candles, liquidations: 2 minutes
- trades: 60 minutes (trades can naturally have longer gaps during low activity periods)
const ws = new OxArchiveWs({
apiKey: '0xa_your_api_key', // Required
wsUrl: 'wss://api.0xarchive.io/ws', // Optional
autoReconnect: true, // Auto-reconnect on disconnect (default: true)
reconnectDelay: 1000, // Initial reconnect delay in ms (default: 1000)
maxReconnectAttempts: 10, // Max reconnect attempts (default: 10)
pingInterval: 30000, // Keep-alive ping interval in ms (default: 30000)
});| Channel | Description | Requires Coin | Live Subscription | Historical Replay |
|---|---|---|---|---|
orderbook |
L2 order book updates | Yes | Yes | Yes |
trades |
Trade/fill updates | Yes | Yes | Yes |
candles |
OHLCV candle data | Yes | No | Yes |
liquidations |
Liquidation events (May 2025+) | Yes | Yes | Yes |
open_interest |
Open interest snapshots | Yes | Yes | Yes |
funding |
Funding rate snapshots | Yes | Yes | Yes |
ticker |
Price and 24h volume | Yes | Yes | No |
all_tickers |
All market tickers | No | Yes | No |
l4_diffs |
L4 orderbook diffs with user attribution | Yes | Yes | Yes |
l4_orders |
Order lifecycle events with user attribution | Yes | Yes | Yes |
Each liquidations data message is a fill row with is_liquidation: true — the wire shape matches trades exactly. Use onLiquidations to receive a parsed Trade[].
Hyperliquid core l4_diffs and l4_orders support bounded replay as an
l4_snapshot followed by ordered l4_batch events. The batches are ordered
by (block_number, seq); pass an explicit end for the bounded request and
the replay speed option is ignored. HIP-3,
HIP-4, and Hyperliquid Spot L4 channels remain live-only.
| Channel | Description | Requires Coin | Live Subscription | Historical Replay |
|---|---|---|---|---|
hip3_orderbook |
HIP-3 L2 order book snapshots | Yes | Yes | Yes |
hip3_trades |
HIP-3 trade/fill updates | Yes | Yes | Yes |
hip3_candles |
HIP-3 OHLCV candle data | Yes | Yes | Yes |
hip3_open_interest |
HIP-3 open interest snapshots | Yes | No | Yes |
hip3_funding |
HIP-3 funding rate snapshots | Yes | No | Yes |
hip3_liquidations |
HIP-3 liquidation events (Feb 2026+) | Yes | Yes | Yes |
hip3_l4_diffs |
HIP-3 L4 orderbook diffs | Yes | Yes | No |
hip3_l4_orders |
HIP-3 order lifecycle events | Yes | Yes | No |
Note: HIP-3 coins are case-sensitive (e.g.,
km:US500,xyz:XYZ100). Do not uppercase them.
| Channel | Description | Requires Coin | Live Subscription | Historical Replay |
|---|---|---|---|---|
hip4_orderbook |
HIP-4 L2 order book snapshots | Yes | No | Yes |
hip4_trades |
HIP-4 trade/fill updates | Yes | Yes | Yes |
hip4_open_interest |
HIP-4 open interest (per side) | Yes | No | Yes |
hip4_l4_diffs |
HIP-4 L4 orderbook diffs | Yes | Yes | No |
hip4_l4_orders |
HIP-4 order lifecycle events | Yes | Yes | No |
HIP-4 has no funding or liquidation channel. Candle history and current outcome-side OI are available through REST from 2026-05-02; OI updates at ~10s. Stored WebSocket replay is available for HIP-4 order-book and OI history, but their live bridges are paused. HIP-4 mark_price, midPrice, and candle OHLC values are implied probabilities in [0, 1], not USD prices.
HIP-4 coins can be passed in either the bare numeric form ('0', '1') or the canonical #-prefixed form ('#0', '#1'). The SDK URL-encodes # to %23 on the wire so the path survives fetch parsing — pass whichever form is convenient.
ws.onTrades((coin, trades) => {
console.log(`${coin}: ${trades.length} HIP-4 fills`);
});
ws.subscribeHip4('hip4_trades', '#1');
// Settlement signal — terminal for the coin
ws.onOutcomeSettled((coin, outcomeId, side, value, at) => {
console.log(`${coin} (outcome ${outcomeId} side ${side}) settled: ${value} at ${at}`);
});| Channel | Description | Requires Coin | Live Subscription | Historical Replay |
|---|---|---|---|---|
spot_orderbook |
Spot L2 order book snapshots | Yes | Yes | No |
spot_trades |
Spot trade/fill updates | Yes | Yes | No |
spot_l4_diffs |
Spot L4 orderbook diffs | Yes | Yes | No |
spot_l4_orders |
Spot order lifecycle events | Yes | Yes | No |
spot_twap |
Spot TWAP statuses | Yes | Yes | No |
Spot symbols are dashed canonical (HYPE-USDC, PURR-USDC). The server resolves the dashed form to wire format internally. Spot has no funding, open interest, or liquidations; candle history is REST-only through client.spot.candles and has no spot_candles WebSocket channel.
ws.onOrderbook((coin, ob) => {
console.log(`${coin} mid: ${ob.midPrice}`);
});
ws.onTrades((coin, trades) => {
console.log(`${coin} got ${trades.length} trades`);
});
await ws.connect();
ws.subscribeSpot('orderbook', 'HYPE-USDC');
ws.subscribeSpot('trades', 'HYPE-USDC');
// L4 channels
ws.subscribeSpot('l4_diffs', 'HYPE-USDC');
ws.subscribeSpot('l4_orders', 'HYPE-USDC');
// TWAP statuses
ws.subscribeSpot('twap', 'HYPE-USDC');import { OxArchiveWs } from '@0xarchive/sdk';
const ws = new OxArchiveWs({ apiKey: 'ox_...' });
ws.onLiquidations((channel, coin, fills) => {
for (const f of fills) {
console.log(`${channel} ${coin}: ${f.side} ${f.size}@${f.price} liq`);
}
});
await ws.connect();
ws.subscribeLiquidations('BTC');
ws.subscribeHip3Liquidations('hyna:BTC');| Channel | Description | Requires Coin | Live Subscription | Historical Replay | Current Data Path |
|---|---|---|---|---|---|
lighter_orderbook |
Lighter L2 order book (top 20 levels per side when live) | Yes | Yes | Yes | Live subscription or Lighter REST |
lighter_trades |
Lighter trade/fill updates | Yes | Yes | Yes | Live subscription or Lighter REST |
lighter_candles |
Lighter OHLCV candle data | Yes | No | Yes | Lighter REST |
lighter_open_interest |
Lighter open interest and market context | Yes | Yes | Yes | Live subscription or Lighter REST |
lighter_funding |
Lighter funding rate and market context | Yes | Yes | Yes | Live subscription or Lighter REST |
lighter_l3_orderbook |
Lighter L3 order-level orderbook | Yes | No | Yes | Lighter REST |
Historical Lighter data is available through REST, WebSocket replay, or exports.
Live Lighter data uses the same subscribe, ack, and data envelope as Hyperliquid live data, and the live payloads use Hyperliquid-style shapes. It is available on every plan and is served on wss://api.0xarchive.io/ws, the client's default URL. wss://stream.0xarchive.io/ws serves only Hyperliquid node-sourced channels and answers a Lighter subscribe with an error that points to wss://api.0xarchive.io/ws.
Live Lighter messages are metered per message in the same way as Hyperliquid live data, and the usual plan limits on connections and subscriptions apply, as does the limit of 10 subscribe requests per second per connection. Symbols are the same as client.lighter.instruments.list(); they are case-insensitive when you subscribe and the server echoes them uppercase.
import { OxArchiveWs } from '@0xarchive/sdk';
const ws = new OxArchiveWs({ apiKey: '0xa_your_api_key' });
// Full top-20 book, as published
ws.onLighterOrderbook((coin, book) => {
const [bids, asks] = book.levels;
console.log(`${coin} ${bids[0]?.px} / ${asks[0]?.px} at ${book.time}`);
});
// Trade legs, as published: two legs per trade, sharing tid
ws.onLighterTrades((coin, legs) => {
const trades = new Set(legs.map((leg) => leg.tid)).size;
console.log(`${coin}: ${trades} trades`);
});
// lighter_open_interest and lighter_funding carry the same message
ws.onLighterStats((channel, coin, stats) => {
console.log(`${channel} ${coin}: OI ${stats.ctx.openInterest}, funding ${stats.ctx.funding}`);
});
// Server errors, including lag notices, arrive as { type: 'error' } messages
ws.on('onMessage', (message) => {
if (message.type === 'error') console.warn(message.message);
});
await ws.connect();
ws.subscribeLighter('orderbook', 'BTC'); // one book a second
ws.subscribeLighter('orderbook', 'ETH', { intervalMs: 250 }); // at most one book every 250 ms
ws.subscribeLighter('trades', 'BTC');
ws.subscribeLighter('open_interest', 'BTC');
ws.subscribeLighter('funding', 'BTC');
// Equivalent generic form; intervalMs is sent on the wire as interval_ms
ws.subscribe('lighter_orderbook', 'SOL', { intervalMs: 500 });
ws.unsubscribeLighter('trades', 'BTC');While an onLighterOrderbook or onLighterTrades handler is registered, Lighter books or trades go only to that handler, so Lighter BTC is not mixed with Hyperliquid BTC in onOrderbook or onTrades. Without one, onOrderbook and onTrades receive live Lighter data converted to the SDK OrderBook and Trade types; each converted Lighter trade is one leg and carries its Lighter account index in accountIndex.
lighter_orderbook (LighterLiveOrderbook), shown truncated to two levels per side:
{"coin":"BTC","time":1790294171459,"levels":[[{"px":"84368.7","sz":"0.00020","n":1},{"px":"84368.6","sz":"0.00020","n":1}],[{"px":"84368.8","sz":"0.05720","n":1},{"px":"84368.9","sz":"0.14223","n":1}]]}levels[0]holds bids, best (highest) first;levels[1]holds asks, best (lowest) first; up to 20 levels per side.- Every message is a full book, not a diff.
timeis Lighter's book update time in milliseconds. pxandszare decimal strings exactly as Lighter publishes them.nis always 1 because Lighter does not publish per-level order counts.- The newest book is sent at most once per interval: one second by default, or
intervalMsfrom 100 to 5000 (inclusive). Each book sent is one metered message.intervalMsis accepted only onlighter_orderbook, and the SDK throws before sending when it is used on another channel or is out of range. - On subscribe, the current book is sent immediately when one is available. Illiquid markets can go minutes without a change.
lighter_trades (LighterLiveTrade[])
[{"coin":"BTC","side":"A","px":"84367.9","sz":"0.00003","time":1790294182211,"hash":"0000001dc8774b28000001a0d5d94943000000000000000000000000000000000000000000000000","tid":31944180930,"oid":562953419896990,"crossed":false,"dir":null,"fee":null,"fee_token":null,"closed_pnl":null,"start_position":"109.79011","users":["281474976623827"]},
{"coin":"BTC","side":"B","px":"84367.9","sz":"0.00003","time":1790294182211,"hash":"0000001dc8774b28000001a0d5d94943000000000000000000000000000000000000000000000000","tid":31944180930,"oid":844421425107071,"crossed":true,"dir":null,"fee":null,"fee_token":null,"closed_pnl":null,"start_position":"0.03940","users":["713845"]}]- Each trade arrives as two legs, one per side, with the same
tid. Count trades by distincttid, not by array length, and compute volume as the sum ofszover one leg pertid. sideis"A"for the ask side and"B"for the bid side.crossed: truemarks the taker leg.usersholds that side's Lighter account index as a string,oidis that side's order id, andstart_positionis that account's signed position before the trade.hashis the Lighter transaction hash andtimeis in milliseconds.fee,fee_token,closed_pnl, anddirare alwaysnullin live messages because Lighter's live stream does not carry them.- Live trades are delivered as they happen, typically batched within about 100 ms, and are preliminary. The finalized record, with fields the live stream does not carry such as fees, is served by
client.lighter.trades.list()(GET /v1/lighter/trades/{symbol}), which returns only reconciled trades (seemeta.finalized_through).client.lighter.trades.recent()serves the preliminary tier.
lighter_open_interest and lighter_funding (LighterLiveStats)
Both channels carry the same message:
{"coin":"BTC","ctx":{"openInterest":"172706178.266310","funding":"0.000012","premium":"-0.000327","markPx":"84363.5","oraclePx":"84397.0","midPx":"84368.8","dayNtlVlm":"908611371.550746","dayBaseVlm":"10808.97087","prevDayPx":"84285.9","impactPxs":null}}openInterestis Lighter's reported open interest, the same value asopen_interestfromGET /v1/lighter/openinterest/{symbol}/current.fundingis Lighter's current funding rate as a fraction, the same as the RESTfunding_rate(Lighter publishes percent, which is divided by 100).premiumis also a fraction.markPxis the mark price,oraclePxis Lighter's index price, andmidPxis the mid price.dayNtlVlmis 24h quote volume anddayBaseVlmis 24h base volume.prevDayPxis derived from the last trade price and Lighter's 24h percent change.impactPxsis alwaysnullbecause Lighter has no impact prices.- Updates arrive as Lighter publishes them, about once per second per market. On subscribe, the latest values are sent immediately when available.
Falling behind. A client that falls behind lighter_trades, lighter_open_interest, or lighter_funding receives an error message such as Dropped ~N live lighter_trades messages for BTC: your connection fell behind the Lighter stream, and those trades were not delivered. If the lag persists, the server stops that subscription with Stopped the lighter_trades stream for BTC: your connection is too slow to keep up. Re-subscribe to resume. lighter_orderbook sends the newest book at each interval and never sends an older book after a newer one.
Replay is unchanged. All six Lighter channels still support replay, and replay keeps its existing historical_data row shapes. Those rows are not the live shapes above, so handle them separately in onHistoricalData.
// Replay candles at 10x speed
ws.replay('candles', 'BTC', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 10,
interval: '15m' // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
});
// Lighter.xyz candles (replay only; use REST for current data)
ws.replay('lighter_candles', 'BTC', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 10,
interval: '5m'
});// Replay HIP-3 orderbook at 50x speed
ws.replay('hip3_orderbook', 'km:US500', {
start: Date.now() - 3600000,
end: Date.now(),
speed: 50,
});
// HIP-3 candles
ws.replay('hip3_candles', 'km:US500', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 100,
interval: '1h'
});Replay multiple data channels simultaneously with synchronized timing. Data from all channels is interleaved chronologically. Before the timeline begins, replay_snapshot messages provide the initial state for each channel at the start timestamp.
const ws = new OxArchiveWs({ apiKey: 'ox_...' });
await ws.connect();
// Handle initial snapshots (sent before timeline data)
ws.onReplaySnapshot((channel, coin, timestamp, data) => {
console.log(`Initial ${channel} state at ${new Date(timestamp).toISOString()}`);
if (channel === 'orderbook') {
currentOrderbook = data;
} else if (channel === 'funding') {
currentFundingRate = data;
} else if (channel === 'open_interest') {
currentOI = data;
}
});
// Handle interleaved historical data
ws.onHistoricalData((coin, timestamp, data) => {
// The `channel` field on the raw message indicates which channel
// this data point belongs to
console.log(`${new Date(timestamp).toISOString()}: data received`);
});
ws.onReplayComplete((channel, coin, count) => {
console.log(`Replay complete: ${count} records`);
});
// Start multi-channel replay
ws.multiReplay(['orderbook', 'trades', 'funding'], 'BTC', {
start: Date.now() - 86400000,
end: Date.now(),
speed: 10
});
// Playback controls work the same as single-channel
ws.replayPause();
ws.replayResume();
ws.replaySeek(1704067200000);
ws.replayStop();Channels available for multi-channel replay: Standard replay channels can be combined in a single multi-channel replay. Core Hyperliquid L4 replay is single-channel. HIP-3, HIP-4, and Hyperliquid Spot L4 channels remain live-only.
ws.getState(); // 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
ws.isConnected(); // booleanThe SDK accepts timestamps as Unix milliseconds or Date objects:
// Unix milliseconds (recommended)
client.hyperliquid.orderbook.history('BTC', {
start: Date.now() - 86400000,
end: Date.now()
});
// Date objects (converted automatically)
client.hyperliquid.orderbook.history('BTC', {
start: new Date('2024-01-01'),
end: new Date('2024-01-02')
});
// WebSocket replay also accepts both
ws.replay('orderbook', 'BTC', {
start: Date.now() - 3600000,
end: Date.now(),
speed: 10
});import { OxArchive, OxArchiveError } from '@0xarchive/sdk';
try {
const orderbook = await client.orderbook.get('INVALID');
} catch (error) {
if (error instanceof OxArchiveError) {
console.error(`API Error: ${error.message}`);
console.error(`Status Code: ${error.code}`);
console.error(`Request ID: ${error.requestId}`);
}
}Full TypeScript support with exported types:
import type {
OrderBook,
PriceLevel,
Trade,
Candle,
Instrument,
LighterInstrument,
Hip3Instrument,
LighterGranularity,
FundingRate,
OpenInterest,
Liquidation,
LiquidationVolume,
CoinFreshness,
CoinSummary,
PriceSnapshot,
CursorResponse,
WsOptions,
WsChannel,
WsConnectionState,
WsReplaySnapshot,
WsSubscribeOptions,
// Live Lighter WebSocket payloads
LighterLiveOrderbook,
LighterLiveTrade,
LighterLiveStats,
// Orderbook reconstruction
OrderbookDelta,
TickData,
ReconstructedOrderBook,
ReconstructOptions,
TickHistoryParams,
} from '@0xarchive/sdk';
// Import reconstructor class
import { OrderBookReconstructor } from '@0xarchive/sdk';Enable Zod schema validation for API responses:
const client = new OxArchive({
apiKey: '0xa_your_api_key',
validate: true // Enable runtime validation
});When enabled, responses are validated against Zod schemas and throw OxArchiveError with status 422 if validation fails.
For large-scale data exports (historical order books, trades, and other datasets), use the Data Catalog. It lets you choose markets, datasets, and date ranges, see a live quote, and export zstd-compressed Parquet.
- Node.js 18+ or modern browsers with
fetchandWebSocketsupport
MIT