@0xarchive/sdk 1.4.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1324 +1,1551 @@
1
- # @0xarchive/sdk
2
-
3
- Official TypeScript/JavaScript SDK for [0xarchive](https://0xarchive.io) - Historical Market Data API.
4
-
5
- Supports multiple exchanges:
6
- - **Hyperliquid** - Perpetuals data from April 2023
7
- - **Hyperliquid HIP-3** - Builder-deployed perpetuals (February 2026+, free tier: km:US500, Build+: all symbols, Pro+: orderbook history)
8
- - **Lighter.xyz** - Perpetuals data (August 2025+ for fills, Jan 2026+ for OB, OI, Funding Rate)
9
-
10
- ## Installation
11
-
12
- ```bash
13
- npm install @0xarchive/sdk
14
- # or
15
- yarn add @0xarchive/sdk
16
- # or
17
- pnpm add @0xarchive/sdk
18
- ```
19
-
20
- ## Quick Start
21
-
22
- ```typescript
23
- import { OxArchive } from '@0xarchive/sdk';
24
-
25
- const client = new OxArchive({ apiKey: '0xa_your_api_key' });
26
-
27
- // Hyperliquid data
28
- const hlOrderbook = await client.hyperliquid.orderbook.get('BTC');
29
- console.log(`Hyperliquid BTC mid price: ${hlOrderbook.midPrice}`);
30
-
31
- // Lighter.xyz data
32
- const lighterOrderbook = await client.lighter.orderbook.get('BTC');
33
- console.log(`Lighter BTC mid price: ${lighterOrderbook.midPrice}`);
34
-
35
- // HIP-3 builder perps (February 2026+)
36
- const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
37
- const hip3Orderbook = await client.hyperliquid.hip3.orderbook.get('km:US500');
38
- const hip3Trades = await client.hyperliquid.hip3.trades.recent('km:US500');
39
- const hip3Funding = await client.hyperliquid.hip3.funding.current('xyz:XYZ100');
40
- const hip3Oi = await client.hyperliquid.hip3.openInterest.current('xyz:XYZ100');
41
-
42
- // Get historical order book snapshots
43
- const history = await client.hyperliquid.orderbook.history('ETH', {
44
- start: Date.now() - 86400000, // 24 hours ago
45
- end: Date.now(),
46
- limit: 100
47
- });
48
- ```
49
-
50
- ## Configuration
51
-
52
- ```typescript
53
- const client = new OxArchive({
54
- apiKey: '0xa_your_api_key', // Required
55
- baseUrl: 'https://api.0xarchive.io', // Optional
56
- timeout: 30000, // Optional, request timeout in ms (default: 30000)
57
- validate: false, // Optional, enable Zod schema validation
58
- });
59
- ```
60
-
61
- ## REST API Reference
62
-
63
- Core resources (orderbook, trades, instruments, funding, openInterest, candles, freshness, summary, priceHistory) are available on both `client.hyperliquid.*` and `client.lighter.*`. Some resources are exchange-specific -- see each section for details.
64
-
65
- ### Order Book
66
-
67
- ```typescript
68
- // Get current order book (Hyperliquid)
69
- const orderbook = await client.hyperliquid.orderbook.get('BTC');
70
-
71
- // Get current order book (Lighter.xyz)
72
- const lighterOb = await client.lighter.orderbook.get('BTC');
73
-
74
- // Get order book at specific timestamp with custom depth
75
- const historical = await client.hyperliquid.orderbook.get('BTC', {
76
- timestamp: 1704067200000,
77
- depth: 20 // Number of levels per side
78
- });
79
-
80
- // Get historical snapshots (start is required)
81
- const history = await client.hyperliquid.orderbook.history('BTC', {
82
- start: Date.now() - 86400000,
83
- end: Date.now(),
84
- limit: 1000
85
- });
86
- ```
87
-
88
- #### Orderbook Depth Limits
89
-
90
- The `depth` parameter controls how many price levels are returned per side. Tier-based limits apply:
91
-
92
- | Tier | Max Depth |
93
- |------|-----------|
94
- | Free | 20 |
95
- | Build | 200 |
96
- | Pro | Full Depth |
97
- | Enterprise | Full Depth |
98
-
99
- **Note:** Hyperliquid L2 source data contains 20 levels. Full-depth L2 (derived from L4) and Lighter.xyz provide full depth on Pro+. Depth limits apply to L2 snapshot endpoints only — L4 and L2 diff endpoints return full data.
100
-
101
- #### Lighter Orderbook Granularity
102
-
103
- Lighter.xyz orderbook history supports a `granularity` parameter for different data resolutions. Tier restrictions apply.
104
-
105
- | Granularity | Interval | Tier Required | Credit Multiplier |
106
- |-------------|----------|---------------|-------------------|
107
- | `checkpoint` | ~60s | Free+ | 1x |
108
- | `30s` | 30s | Build+ | 2x |
109
- | `10s` | 10s | Build+ | 3x |
110
- | `1s` | 1s | Pro+ | 10x |
111
- | `tick` | tick-level | Enterprise | 20x |
112
-
113
- ```typescript
114
- // Get Lighter orderbook history with 10s resolution (Build+ tier)
115
- const history = await client.lighter.orderbook.history('BTC', {
116
- start: Date.now() - 86400000,
117
- end: Date.now(),
118
- granularity: '10s'
119
- });
120
-
121
- // Get 1-second resolution (Pro+ tier)
122
- const history = await client.lighter.orderbook.history('BTC', {
123
- start: Date.now() - 86400000,
124
- end: Date.now(),
125
- granularity: '1s'
126
- });
127
-
128
- // Tick-level data (Enterprise tier) - returns checkpoint + raw deltas
129
- const history = await client.lighter.orderbook.history('BTC', {
130
- start: Date.now() - 86400000,
131
- end: Date.now(),
132
- granularity: 'tick'
133
- });
134
- ```
135
-
136
- **Note:** The `granularity` parameter is ignored for Hyperliquid orderbook history.
137
-
138
- #### Orderbook Reconstruction (Enterprise Tier)
139
-
140
- For tick-level data, the SDK provides client-side orderbook reconstruction. This efficiently reconstructs full orderbook state from a checkpoint and incremental deltas.
141
-
142
- ```typescript
143
- import { OrderBookReconstructor } from '@0xarchive/sdk';
144
-
145
- // Option 1: Get fully reconstructed snapshots (simplest)
146
- const snapshots = await client.lighter.orderbook.historyReconstructed('BTC', {
147
- start: Date.now() - 3600000,
148
- end: Date.now()
149
- });
150
-
151
- for (const ob of snapshots) {
152
- console.log(`${ob.timestamp}: bid=${ob.bids[0]?.px} ask=${ob.asks[0]?.px}`);
153
- }
154
-
155
- // Option 2: Get raw tick data for custom reconstruction
156
- const tickData = await client.lighter.orderbook.historyTick('BTC', {
157
- start: Date.now() - 3600000,
158
- end: Date.now()
159
- });
160
-
161
- console.log(`Checkpoint: ${tickData.checkpoint.bids.length} bids`);
162
- console.log(`Deltas: ${tickData.deltas.length} updates`);
163
-
164
- // Option 3: Auto-paginating iterator (recommended for large time ranges)
165
- // Automatically handles pagination, fetching up to 1,000 deltas per request
166
- for await (const snapshot of client.lighter.orderbook.iterateTickHistory('BTC', {
167
- start: Date.now() - 86400000, // 24 hours of data
168
- end: Date.now()
169
- })) {
170
- console.log(snapshot.timestamp, 'Mid:', snapshot.midPrice);
171
- if (someCondition(snapshot)) break; // Early exit supported
172
- }
173
-
174
- // Option 4: Manual iteration (single page, for custom logic)
175
- const reconstructor = client.lighter.orderbook.createReconstructor();
176
- for (const snapshot of reconstructor.iterate(tickData.checkpoint, tickData.deltas)) {
177
- // Process each snapshot without loading all into memory
178
- if (someCondition(snapshot)) break; // Early exit if needed
179
- }
180
-
181
- // Option 5: Get only final state (most efficient)
182
- const final = reconstructor.reconstructFinal(tickData.checkpoint, tickData.deltas);
183
-
184
- // Check for sequence gaps
185
- const gaps = OrderBookReconstructor.detectGaps(tickData.deltas);
186
- if (gaps.length > 0) {
187
- console.warn('Sequence gaps detected:', gaps);
188
- }
189
- ```
190
-
191
- **Methods:**
192
- | Method | Description |
193
- |--------|-------------|
194
- | `historyTick(coin, params)` | Get raw checkpoint + deltas (single page, max 1,000 deltas) |
195
- | `historyReconstructed(coin, params, options)` | Get fully reconstructed snapshots (single page) |
196
- | `iterateTickHistory(coin, params, depth?)` | Auto-paginating async iterator for large time ranges |
197
- | `createReconstructor()` | Create a reconstructor instance for manual control |
198
-
199
- **Note:** The API returns a maximum of 1,000 deltas per request. For time ranges with more deltas, use `iterateTickHistory()` which handles pagination automatically.
200
-
201
- **ReconstructOptions:**
202
- | Option | Default | Description |
203
- |--------|---------|-------------|
204
- | `depth` | all | Maximum price levels in output |
205
- | `emitAll` | `true` | If `false`, only return final state |
206
-
207
- ### Trades
208
-
209
- The trades API uses cursor-based pagination for efficient retrieval of large datasets.
210
-
211
- ```typescript
212
- // Get trade history with cursor-based pagination
213
- let result = await client.hyperliquid.trades.list('BTC', {
214
- start: Date.now() - 86400000,
215
- end: Date.now(),
216
- limit: 1000
217
- });
218
-
219
- // Paginate through all results
220
- const allTrades = [...result.data];
221
- while (result.nextCursor) {
222
- result = await client.hyperliquid.trades.list('BTC', {
223
- start: Date.now() - 86400000,
224
- end: Date.now(),
225
- cursor: result.nextCursor,
226
- limit: 1000
227
- });
228
- allTrades.push(...result.data);
229
- }
230
-
231
- // Get recent trades (Lighter only - has real-time data)
232
- const recent = await client.lighter.trades.recent('BTC', 100);
233
- ```
234
-
235
- **Note:** The `recent()` method is available for Lighter.xyz (`client.lighter.trades.recent()`) and HIP-3 (`client.hyperliquid.hip3.trades.recent()`). Hyperliquid does not have a recent trades endpoint -- use `list()` with a time range instead.
236
-
237
- ### Instruments
238
-
239
- ```typescript
240
- // List all trading instruments (Hyperliquid)
241
- const instruments = await client.hyperliquid.instruments.list();
242
-
243
- // Get specific instrument details
244
- const btc = await client.hyperliquid.instruments.get('BTC');
245
- console.log(`BTC size decimals: ${btc.szDecimals}`);
246
- ```
247
-
248
- #### Lighter.xyz Instruments
249
-
250
- Lighter instruments have a different schema with additional fields for fees, market IDs, and minimum order amounts:
251
-
252
- ```typescript
253
- // List Lighter instruments (returns LighterInstrument, not Instrument)
254
- const lighterInstruments = await client.lighter.instruments.list();
255
-
256
- // Get specific Lighter instrument
257
- const eth = await client.lighter.instruments.get('ETH');
258
- console.log(`ETH taker fee: ${eth.takerFee}`);
259
- console.log(`ETH maker fee: ${eth.makerFee}`);
260
- console.log(`ETH market ID: ${eth.marketId}`);
261
- console.log(`ETH min base amount: ${eth.minBaseAmount}`);
262
- ```
263
-
264
- **Key differences:**
265
- | Field | Hyperliquid (`Instrument`) | Lighter (`LighterInstrument`) |
266
- |-------|---------------------------|------------------------------|
267
- | Symbol | `name` | `symbol` |
268
- | Size decimals | `szDecimals` | `sizeDecimals` |
269
- | Fee info | Not available | `takerFee`, `makerFee`, `liquidationFee` |
270
- | Market ID | Not available | `marketId` |
271
- | Min amounts | Not available | `minBaseAmount`, `minQuoteAmount` |
272
-
273
- #### HIP-3 Instruments
274
-
275
- HIP-3 instruments are derived from live market data and include mark price, open interest, and mid price:
276
-
277
- ```typescript
278
- // List all HIP-3 instruments (no tier restriction)
279
- const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
280
- for (const inst of hip3Instruments) {
281
- console.log(`${inst.coin} (${inst.namespace}:${inst.ticker}): mark=${inst.markPrice}, OI=${inst.openInterest}`);
282
- }
283
-
284
- // Get specific HIP-3 instrument (case-sensitive)
285
- const us500 = await client.hyperliquid.hip3.instruments.get('km:US500');
286
- console.log(`Mark price: ${us500.markPrice}`);
287
- ```
288
-
289
- **Available HIP-3 Coins:**
290
- | Builder | Coins |
291
- |---------|-------|
292
- | xyz (Hyperliquid) | `xyz:XYZ100` |
293
- | km (Kinetiq Markets) | `km:US500`, `km:SMALL2000`, `km:GOOGL`, `km:USBOND`, `km:GOLD`, `km:USTECH`, `km:NVDA`, `km:SILVER`, `km:BABA` |
294
-
295
- ### Funding Rates
296
-
297
- ```typescript
298
- // Get current funding rate
299
- const current = await client.hyperliquid.funding.current('BTC');
300
-
301
- // Get funding rate history (start is required)
302
- const history = await client.hyperliquid.funding.history('ETH', {
303
- start: Date.now() - 86400000 * 7,
304
- end: Date.now()
305
- });
306
-
307
- // Get funding rate history with aggregation interval
308
- const hourly = await client.hyperliquid.funding.history('BTC', {
309
- start: Date.now() - 86400000 * 7,
310
- end: Date.now(),
311
- interval: '1h'
312
- });
313
- ```
314
-
315
- #### Funding History Parameters
316
-
317
- | Parameter | Type | Required | Description |
318
- |-----------|------|----------|-------------|
319
- | `start` | `number \| string` | Yes | Start timestamp (Unix ms or ISO string) |
320
- | `end` | `number \| string` | Yes | End timestamp (Unix ms or ISO string) |
321
- | `cursor` | `number \| string` | No | Cursor from previous response for pagination |
322
- | `limit` | `number` | No | Max results (default: 100, max: 1000) |
323
- | `interval` | `OiFundingInterval` | No | Aggregation interval: `'5m'`, `'15m'`, `'30m'`, `'1h'`, `'4h'`, `'1d'`. When omitted, raw ~1 min data is returned. |
324
-
325
- ### Open Interest
326
-
327
- ```typescript
328
- // Get current open interest
329
- const current = await client.hyperliquid.openInterest.current('BTC');
330
-
331
- // Get open interest history (start is required)
332
- const history = await client.hyperliquid.openInterest.history('ETH', {
333
- start: Date.now() - 86400000,
334
- end: Date.now(),
335
- limit: 100
336
- });
337
-
338
- // Get open interest history with aggregation interval
339
- const hourly = await client.hyperliquid.openInterest.history('BTC', {
340
- start: Date.now() - 86400000,
341
- end: Date.now(),
342
- interval: '1h'
343
- });
344
- ```
345
-
346
- #### Open Interest History Parameters
347
-
348
- | Parameter | Type | Required | Description |
349
- |-----------|------|----------|-------------|
350
- | `start` | `number \| string` | Yes | Start timestamp (Unix ms or ISO string) |
351
- | `end` | `number \| string` | Yes | End timestamp (Unix ms or ISO string) |
352
- | `cursor` | `number \| string` | No | Cursor from previous response for pagination |
353
- | `limit` | `number` | No | Max results (default: 100, max: 1000) |
354
- | `interval` | `OiFundingInterval` | No | Aggregation interval: `'5m'`, `'15m'`, `'30m'`, `'1h'`, `'4h'`, `'1d'`. When omitted, raw ~1 min data is returned. |
355
-
356
- ### Liquidations
357
-
358
- Get historical liquidation events. Data available from May 2025 onwards for Hyperliquid, and from February 2026 for HIP-3.
359
-
360
- ```typescript
361
- // Get liquidation history for a coin (Hyperliquid)
362
- const liquidations = await client.hyperliquid.liquidations.history('BTC', {
363
- start: Date.now() - 86400000,
364
- end: Date.now(),
365
- limit: 100
366
- });
367
-
368
- // Paginate through all results
369
- const allLiquidations = [...liquidations.data];
370
- while (liquidations.nextCursor) {
371
- const next = await client.hyperliquid.liquidations.history('BTC', {
372
- start: Date.now() - 86400000,
373
- end: Date.now(),
374
- cursor: liquidations.nextCursor,
375
- limit: 1000
376
- });
377
- allLiquidations.push(...next.data);
378
- }
379
-
380
- // Get liquidations for a specific user
381
- const userLiquidations = await client.hyperliquid.liquidations.byUser('0x1234...', {
382
- start: Date.now() - 86400000 * 7,
383
- end: Date.now(),
384
- coin: 'BTC' // optional filter
385
- });
386
-
387
- // HIP-3 liquidations (case-sensitive coins)
388
- const hip3Liquidations = await client.hyperliquid.hip3.liquidations.history('km:US500', {
389
- start: Date.now() - 86400000,
390
- end: Date.now(),
391
- limit: 100
392
- });
393
- ```
394
-
395
- ### Liquidation Volume
396
-
397
- 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.
398
-
399
- ```typescript
400
- // Get hourly liquidation volume for the last week (Hyperliquid)
401
- const volume = await client.hyperliquid.liquidations.volume('BTC', {
402
- start: Date.now() - 86400000 * 7,
403
- end: Date.now(),
404
- interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
405
- });
406
-
407
- for (const bucket of volume.data) {
408
- console.log(`${bucket.timestamp}: total=$${bucket.totalUsd}, long=$${bucket.longUsd}, short=$${bucket.shortUsd}`);
409
- }
410
-
411
- // HIP-3 liquidation volume (case-sensitive coins)
412
- const hip3Volume = await client.hyperliquid.hip3.liquidations.volume('km:US500', {
413
- start: Date.now() - 86400000 * 7,
414
- end: Date.now(),
415
- interval: '1h'
416
- });
417
- ```
418
-
419
- ### Orders
420
-
421
- Access order history, order flow aggregations, and TP/SL (take-profit/stop-loss) orders. Available for Hyperliquid and HIP-3.
422
-
423
- ```typescript
424
- // Get order history for a coin
425
- const orders = await client.hyperliquid.orders.history('BTC', {
426
- start: Date.now() - 86400000,
427
- end: Date.now(),
428
- limit: 1000,
429
- user: '0x1234...', // optional: filter by user address
430
- status: 'filled', // optional: filter by status
431
- order_type: 'limit', // optional: filter by order type
432
- });
433
-
434
- // Paginate through all results
435
- const allOrders = [...orders.data];
436
- while (orders.nextCursor) {
437
- const next = await client.hyperliquid.orders.history('BTC', {
438
- start: Date.now() - 86400000,
439
- end: Date.now(),
440
- cursor: orders.nextCursor,
441
- limit: 1000
442
- });
443
- allOrders.push(...next.data);
444
- }
445
-
446
- // Get order flow (aggregated order activity over time)
447
- const flow = await client.hyperliquid.orders.flow('BTC', {
448
- start: Date.now() - 86400000,
449
- end: Date.now(),
450
- interval: '1h', // optional aggregation interval
451
- limit: 100
452
- });
453
-
454
- // Get TP/SL orders
455
- const tpsl = await client.hyperliquid.orders.tpsl('BTC', {
456
- start: Date.now() - 86400000,
457
- end: Date.now(),
458
- user: '0x1234...', // optional: filter by user
459
- triggered: true, // optional: filter by triggered status
460
- });
461
-
462
- // HIP-3 orders (case-sensitive coins)
463
- const hip3Orders = await client.hyperliquid.hip3.orders.history('km:US500', {
464
- start: Date.now() - 86400000,
465
- end: Date.now(),
466
- limit: 1000
467
- });
468
-
469
- const hip3Flow = await client.hyperliquid.hip3.orders.flow('km:US500', {
470
- start: Date.now() - 86400000,
471
- end: Date.now(),
472
- interval: '1h'
473
- });
474
-
475
- const hip3Tpsl = await client.hyperliquid.hip3.orders.tpsl('km:US500', {
476
- start: Date.now() - 86400000,
477
- end: Date.now()
478
- });
479
- ```
480
-
481
- ### L4 Order Book
482
-
483
- Access L4 orderbook snapshots, diffs, and history. L4 data includes user attribution (who placed each order). Available for Hyperliquid and HIP-3.
484
-
485
- ```typescript
486
- // Get current L4 orderbook snapshot
487
- const l4Ob = await client.hyperliquid.l4Orderbook.get('BTC');
488
-
489
- // Get L4 orderbook at a specific timestamp with custom depth
490
- const l4Historical = await client.hyperliquid.l4Orderbook.get('BTC', {
491
- timestamp: 1704067200000,
492
- depth: 20
493
- });
494
-
495
- // Get L4 orderbook diffs (incremental updates)
496
- const diffs = await client.hyperliquid.l4Orderbook.diffs('BTC', {
497
- start: Date.now() - 3600000,
498
- end: Date.now(),
499
- limit: 1000
500
- });
501
-
502
- // Paginate through diffs
503
- const allDiffs = [...diffs.data];
504
- while (diffs.nextCursor) {
505
- const next = await client.hyperliquid.l4Orderbook.diffs('BTC', {
506
- start: Date.now() - 3600000,
507
- end: Date.now(),
508
- cursor: diffs.nextCursor,
509
- limit: 1000
510
- });
511
- allDiffs.push(...next.data);
512
- }
513
-
514
- // Get L4 orderbook history (full snapshots over time)
515
- const l4History = await client.hyperliquid.l4Orderbook.history('BTC', {
516
- start: Date.now() - 86400000,
517
- end: Date.now(),
518
- limit: 1000
519
- });
520
-
521
- // HIP-3 L4 orderbook (case-sensitive coins)
522
- const hip3L4 = await client.hyperliquid.hip3.l4Orderbook.get('km:US500');
523
-
524
- const hip3L4Diffs = await client.hyperliquid.hip3.l4Orderbook.diffs('km:US500', {
525
- start: Date.now() - 3600000,
526
- end: Date.now(),
527
- limit: 1000
528
- });
529
-
530
- const hip3L4History = await client.hyperliquid.hip3.l4Orderbook.history('km:US500', {
531
- start: Date.now() - 86400000,
532
- end: Date.now(),
533
- limit: 1000
534
- });
535
- ```
536
-
537
- ### L3 Order Book (Lighter only)
538
-
539
- Access L3 orderbook snapshots and history from Lighter.xyz. L3 data includes individual order-level detail.
540
-
541
- ```typescript
542
- // Get current L3 orderbook
543
- const l3Ob = await client.lighter.l3Orderbook.get('BTC');
544
-
545
- // Get L3 orderbook at a specific timestamp with custom depth
546
- const l3Historical = await client.lighter.l3Orderbook.get('BTC', {
547
- timestamp: 1704067200000,
548
- depth: 20
549
- });
550
-
551
- // Get L3 orderbook history
552
- const l3History = await client.lighter.l3Orderbook.history('BTC', {
553
- start: Date.now() - 86400000,
554
- end: Date.now(),
555
- limit: 1000
556
- });
557
-
558
- // Paginate through L3 history
559
- const allL3 = [...l3History.data];
560
- while (l3History.nextCursor) {
561
- const next = await client.lighter.l3Orderbook.history('BTC', {
562
- start: Date.now() - 86400000,
563
- end: Date.now(),
564
- cursor: l3History.nextCursor,
565
- limit: 1000
566
- });
567
- allL3.push(...next.data);
568
- }
569
- ```
570
-
571
- ### L2 Order Book (Full-Depth)
572
-
573
- Access L2 full-depth orderbook derived from L4 data. Available for Hyperliquid and HIP-3.
574
-
575
- ```typescript
576
- // L2 full-depth orderbook (Build+ tier)
577
- const l2 = await client.hyperliquid.l2Orderbook.get('BTC');
578
-
579
- // L2 orderbook at a specific timestamp with depth
580
- const l2Historical = await client.hyperliquid.l2Orderbook.get('BTC', {
581
- timestamp: 1704067200000,
582
- depth: 50
583
- });
584
-
585
- // L2 orderbook history (Build+ tier)
586
- const l2History = await client.hyperliquid.l2Orderbook.history('BTC', {
587
- start: Date.now() - 86400000,
588
- end: Date.now(),
589
- limit: 1000
590
- });
591
-
592
- // L2 tick-level diffs (Pro+ tier)
593
- const l2Diffs = await client.hyperliquid.l2Orderbook.diffs('BTC', {
594
- start: Date.now() - 3600000,
595
- end: Date.now(),
596
- limit: 1000
597
- });
598
-
599
- // HIP-3 L2 orderbook
600
- const hip3L2 = await client.hyperliquid.hip3.l2Orderbook.get('km:US500');
601
- ```
602
-
603
- ### Freshness
604
-
605
- Check when each data type was last updated for a specific coin. Useful for verifying data recency before pulling it. Available for all exchanges.
606
-
607
- ```typescript
608
- // Hyperliquid
609
- const freshness = await client.hyperliquid.freshness('BTC');
610
- console.log(`Orderbook last updated: ${freshness.orderbook.lastUpdated}, lag: ${freshness.orderbook.lagMs}ms`);
611
- console.log(`Trades last updated: ${freshness.trades.lastUpdated}, lag: ${freshness.trades.lagMs}ms`);
612
- console.log(`Funding last updated: ${freshness.funding.lastUpdated}`);
613
- console.log(`OI last updated: ${freshness.openInterest.lastUpdated}`);
614
-
615
- // Lighter.xyz
616
- const lighterFreshness = await client.lighter.freshness('BTC');
617
-
618
- // HIP-3 (case-sensitive coins)
619
- const hip3Freshness = await client.hyperliquid.hip3.freshness('km:US500');
620
- ```
621
-
622
- ### Summary
623
-
624
- Get a combined market snapshot in a single call -- mark/oracle price, funding rate, open interest, 24h volume, and 24h liquidation volumes.
625
-
626
- ```typescript
627
- // Hyperliquid (includes volume + liquidation data)
628
- const summary = await client.hyperliquid.summary('BTC');
629
- console.log(`Mark price: ${summary.markPrice}`);
630
- console.log(`Oracle price: ${summary.oraclePrice}`);
631
- console.log(`Funding rate: ${summary.fundingRate}`);
632
- console.log(`Open interest: ${summary.openInterest}`);
633
- console.log(`24h volume: ${summary.volume24h}`);
634
- console.log(`24h liquidation volume: $${summary.liquidationVolume24h}`);
635
- console.log(` Long: $${summary.longLiquidationVolume24h}`);
636
- console.log(` Short: $${summary.shortLiquidationVolume24h}`);
637
-
638
- // Lighter.xyz (price, funding, OI — no volume/liquidation data)
639
- const lighterSummary = await client.lighter.summary('BTC');
640
-
641
- // HIP-3 (includes mid_price — case-sensitive coins)
642
- const hip3Summary = await client.hyperliquid.hip3.summary('km:US500');
643
- console.log(`Mid price: ${hip3Summary.midPrice}`);
644
- ```
645
-
646
- ### Price History
647
-
648
- Get mark, oracle, and mid price history over time. Supports aggregation intervals. Data projected from open interest records.
649
-
650
- ```typescript
651
- // Hyperliquid available from May 2023
652
- const prices = await client.hyperliquid.priceHistory('BTC', {
653
- start: Date.now() - 86400000,
654
- end: Date.now(),
655
- interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
656
- });
657
-
658
- for (const snapshot of prices.data) {
659
- console.log(`${snapshot.timestamp}: mark=${snapshot.markPrice}, oracle=${snapshot.oraclePrice}, mid=${snapshot.midPrice}`);
660
- }
661
-
662
- // Lighter.xyz
663
- const lighterPrices = await client.lighter.priceHistory('BTC', {
664
- start: Date.now() - 86400000,
665
- end: Date.now(),
666
- interval: '1h'
667
- });
668
-
669
- // HIP-3 (case-sensitive coins)
670
- const hip3Prices = await client.hyperliquid.hip3.priceHistory('km:US500', {
671
- start: Date.now() - 86400000,
672
- end: Date.now(),
673
- interval: '1d'
674
- });
675
-
676
- // Paginate for larger ranges
677
- let result = await client.hyperliquid.priceHistory('BTC', {
678
- start: Date.now() - 86400000 * 30,
679
- end: Date.now(),
680
- interval: '4h',
681
- limit: 1000
682
- });
683
- while (result.nextCursor) {
684
- result = await client.hyperliquid.priceHistory('BTC', {
685
- start: Date.now() - 86400000 * 30,
686
- end: Date.now(),
687
- interval: '4h',
688
- cursor: result.nextCursor,
689
- limit: 1000
690
- });
691
- }
692
- ```
693
-
694
- ### Candles (OHLCV)
695
-
696
- Get historical OHLCV candle data aggregated from trades.
697
-
698
- ```typescript
699
- // Get candle history (start is required)
700
- const candles = await client.hyperliquid.candles.history('BTC', {
701
- start: Date.now() - 86400000,
702
- end: Date.now(),
703
- interval: '1h', // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
704
- limit: 100
705
- });
706
-
707
- // Iterate through candles
708
- for (const candle of candles.data) {
709
- console.log(`${candle.timestamp}: O=${candle.open} H=${candle.high} L=${candle.low} C=${candle.close} V=${candle.volume}`);
710
- }
711
-
712
- // Cursor-based pagination for large datasets
713
- let result = await client.hyperliquid.candles.history('BTC', {
714
- start: Date.now() - 86400000,
715
- end: Date.now(),
716
- interval: '1m',
717
- limit: 1000
718
- });
719
- const allCandles = [...result.data];
720
- while (result.nextCursor) {
721
- result = await client.hyperliquid.candles.history('BTC', {
722
- start: Date.now() - 86400000,
723
- end: Date.now(),
724
- interval: '1m',
725
- cursor: result.nextCursor,
726
- limit: 1000
727
- });
728
- allCandles.push(...result.data);
729
- }
730
-
731
- // Lighter.xyz candles
732
- const lighterCandles = await client.lighter.candles.history('BTC', {
733
- start: Date.now() - 86400000,
734
- end: Date.now(),
735
- interval: '15m'
736
- });
737
- ```
738
-
739
- #### Available Intervals
740
-
741
- | Interval | Description |
742
- |----------|-------------|
743
- | `1m` | 1 minute |
744
- | `5m` | 5 minutes |
745
- | `15m` | 15 minutes |
746
- | `30m` | 30 minutes |
747
- | `1h` | 1 hour (default) |
748
- | `4h` | 4 hours |
749
- | `1d` | 1 day |
750
- | `1w` | 1 week |
751
-
752
- ### Data Quality Monitoring
753
-
754
- Monitor data coverage, incidents, latency, and SLA compliance across all exchanges.
755
-
756
- ```typescript
757
- // Get overall system health status
758
- const status = await client.dataQuality.status();
759
- console.log(`System status: ${status.status}`);
760
- for (const [exchange, info] of Object.entries(status.exchanges)) {
761
- console.log(` ${exchange}: ${info.status}`);
762
- }
763
-
764
- // Get data coverage summary for all exchanges
765
- const coverage = await client.dataQuality.coverage();
766
- for (const exchange of coverage.exchanges) {
767
- console.log(`${exchange.exchange}:`);
768
- for (const [dtype, info] of Object.entries(exchange.dataTypes)) {
769
- console.log(` ${dtype}: ${info.totalRecords.toLocaleString()} records, ${info.completeness}% complete`);
770
- }
771
- }
772
-
773
- // Get symbol-specific coverage with gap detection
774
- const btc = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC');
775
- const oi = btc.dataTypes.open_interest;
776
- console.log(`BTC OI completeness: ${oi.completeness}%`);
777
- console.log(`Historical coverage: ${oi.historicalCoverage}%`); // Hour-level granularity
778
- console.log(`Gaps found: ${oi.gaps.length}`);
779
- for (const gap of oi.gaps.slice(0, 5)) {
780
- console.log(` ${gap.durationMinutes} min gap: ${gap.start} -> ${gap.end}`);
781
- }
782
-
783
- // Check empirical data cadence (when available)
784
- const ob = btc.dataTypes.orderbook;
785
- if (ob.cadence) {
786
- console.log(`Orderbook cadence: ~${ob.cadence.medianIntervalSeconds}s median, p95=${ob.cadence.p95IntervalSeconds}s`);
787
- }
788
-
789
- // Time-bounded gap detection (last 7 days)
790
- const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
791
- const btc7d = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC', {
792
- from: weekAgo,
793
- to: Date.now(),
794
- });
795
-
796
- // List incidents with filtering
797
- const result = await client.dataQuality.listIncidents({ status: 'open' });
798
- for (const incident of result.incidents) {
799
- console.log(`[${incident.severity}] ${incident.title}`);
800
- }
801
-
802
- // Get latency metrics
803
- const latency = await client.dataQuality.latency();
804
- for (const [exchange, metrics] of Object.entries(latency.exchanges)) {
805
- console.log(`${exchange}: OB lag ${metrics.dataFreshness.orderbookLagMs}ms`);
806
- }
807
-
808
- // Get SLA compliance metrics for a specific month
809
- const sla = await client.dataQuality.sla({ year: 2026, month: 1 });
810
- console.log(`Period: ${sla.period}`);
811
- console.log(`Uptime: ${sla.actual.uptime}% (${sla.actual.uptimeStatus})`);
812
- console.log(`API P99: ${sla.actual.apiLatencyP99Ms}ms (${sla.actual.latencyStatus})`);
813
- ```
814
-
815
- #### Data Quality Endpoints
816
-
817
- | Method | Description |
818
- |--------|-------------|
819
- | `status()` | Overall system health and per-exchange status |
820
- | `coverage()` | Data coverage summary for all exchanges |
821
- | `exchangeCoverage(exchange)` | Coverage details for a specific exchange |
822
- | `symbolCoverage(exchange, symbol, options?)` | Coverage with gap detection, cadence, and historical coverage |
823
- | `listIncidents(params)` | List incidents with filtering and pagination |
824
- | `getIncident(incidentId)` | Get specific incident details |
825
- | `latency()` | Current latency metrics (WebSocket, REST, data freshness) |
826
- | `sla(params)` | SLA compliance metrics for a specific month |
827
-
828
- **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:
829
-
830
- ```typescript
831
- const client = new OxArchive({
832
- apiKey: '0xa_your_api_key',
833
- timeout: 60000 // 60 seconds for data quality endpoints
834
- });
835
- ```
836
-
837
- ### Web3 Authentication
838
-
839
- Get API keys programmatically using an Ethereum wallet — no browser or email required.
840
-
841
- #### Free Tier (SIWE)
842
-
843
- ```typescript
844
- import { createWalletClient, http } from 'viem';
845
- import { privateKeyToAccount } from 'viem/accounts';
846
- import { mainnet } from 'viem/chains';
847
-
848
- const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
849
- const walletClient = createWalletClient({ account, chain: mainnet, transport: http() });
850
-
851
- // 1. Get SIWE challenge
852
- const challenge = await client.web3.challenge(account.address);
853
-
854
- // 2. Sign with personal_sign (EIP-191)
855
- const signature = await walletClient.signMessage({ message: challenge.message });
856
-
857
- // 3. Submit receive API key
858
- const result = await client.web3.signup(challenge.message, signature);
859
- console.log(result.apiKey); // "0xa_..."
860
- ```
861
-
862
- #### Paid Tier (x402 USDC on Base)
863
-
864
- ```typescript
865
- import { createWalletClient, http, encodePacked } from 'viem';
866
- import { privateKeyToAccount } from 'viem/accounts';
867
- import { base } from 'viem/chains';
868
- import crypto from 'crypto';
869
-
870
- const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
871
- const walletClient = createWalletClient({ account, chain: base, transport: http() });
872
-
873
- const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
874
-
875
- // 1. Get pricing
876
- const quote = await client.web3.subscribeQuote('build');
877
- // quote.amount = "49000000" ($49 USDC), quote.payTo = "0x..."
878
-
879
- // 2. Build & sign EIP-3009 transferWithAuthorization
880
- const nonce = `0x${crypto.randomBytes(32).toString('hex')}` as `0x${string}`;
881
- const validAfter = 0n;
882
- const validBefore = BigInt(Math.floor(Date.now() / 1000) + 3600);
883
-
884
- const signature = await walletClient.signTypedData({
885
- domain: {
886
- name: 'USD Coin',
887
- version: '2',
888
- chainId: 8453,
889
- verifyingContract: USDC_ADDRESS,
890
- },
891
- types: {
892
- TransferWithAuthorization: [
893
- { name: 'from', type: 'address' },
894
- { name: 'to', type: 'address' },
895
- { name: 'value', type: 'uint256' },
896
- { name: 'validAfter', type: 'uint256' },
897
- { name: 'validBefore', type: 'uint256' },
898
- { name: 'nonce', type: 'bytes32' },
899
- ],
900
- },
901
- primaryType: 'TransferWithAuthorization',
902
- message: {
903
- from: account.address,
904
- to: quote.payTo as `0x${string}`,
905
- value: BigInt(quote.amount),
906
- validAfter,
907
- validBefore,
908
- nonce,
909
- },
910
- });
911
-
912
- // 3. Build x402 payment envelope and base64-encode
913
- const paymentPayload = btoa(JSON.stringify({
914
- x402Version: 2,
915
- payload: {
916
- signature,
917
- authorization: {
918
- from: account.address,
919
- to: quote.payTo,
920
- value: quote.amount,
921
- validAfter: '0',
922
- validBefore: validBefore.toString(),
923
- nonce,
924
- },
925
- },
926
- }));
927
-
928
- // 4. Submit payment receive API key + subscription
929
- const sub = await client.web3.subscribe('build', paymentPayload);
930
- console.log(sub.apiKey, sub.tier, sub.expiresAt);
931
- ```
932
-
933
- #### Key Management
934
-
935
- ```typescript
936
- // List and revoke keys (requires a fresh SIWE signature)
937
- const keys = await client.web3.listKeys(challenge.message, signature);
938
- await client.web3.revokeKey(challenge.message, signature, keys.keys[0].id);
939
- ```
940
-
941
- ### Legacy API (Deprecated)
942
-
943
- The following legacy methods are deprecated and will be removed in v2.0. They default to Hyperliquid data:
944
-
945
- ```typescript
946
- // Deprecated - use client.hyperliquid.orderbook.get() instead
947
- const orderbook = await client.orderbook.get('BTC');
948
-
949
- // Deprecated - use client.hyperliquid.trades.list() instead
950
- const trades = await client.trades.list('BTC', { start, end });
951
- ```
952
-
953
- ## WebSocket Client
954
-
955
- The WebSocket client supports two modes: real-time streaming and historical replay. For bulk data downloads, use the S3 Parquet bulk export via the [Data Explorer](https://0xarchive.io/data).
956
-
957
- ```typescript
958
- import { OxArchiveWs } from '@0xarchive/sdk';
959
-
960
- const ws = new OxArchiveWs({ apiKey: '0xa_your_api_key' });
961
- ```
962
-
963
- ### Real-time Streaming
964
-
965
- Subscribe to live market data from Hyperliquid.
966
-
967
- ```typescript
968
- ws.connect({
969
- onOpen: () => console.log('Connected'),
970
- onClose: (code, reason) => console.log(`Disconnected: ${code}`),
971
- onError: (error) => console.error('Error:', error),
972
- });
973
-
974
- // Subscribe to channels
975
- ws.subscribeOrderbook('BTC');
976
- ws.subscribeTrades('ETH');
977
- ws.subscribeTicker('SOL');
978
- ws.subscribeAllTickers();
979
-
980
- // Handle real-time data with typed callbacks
981
- ws.onOrderbook((coin, data) => {
982
- console.log(`${coin} mid price: ${data.midPrice}`);
983
- });
984
-
985
- ws.onTrades((coin, trades) => {
986
- console.log(`${coin} new trades: ${trades.length}`);
987
- });
988
-
989
- // Unsubscribe when done
990
- ws.unsubscribeOrderbook('BTC');
991
-
992
- // Disconnect
993
- ws.disconnect();
994
- ```
995
-
996
- ### Historical Replay
997
-
998
- Replay historical data with original timing preserved. Perfect for backtesting.
999
-
1000
- > **Important:** Replay data is delivered via `onHistoricalData()`, NOT `onTrades()` or `onOrderbook()`.
1001
- > The real-time callbacks only receive live market data from subscriptions.
1002
-
1003
- ```typescript
1004
- const ws = new OxArchiveWs({ apiKey: 'ox_...' });
1005
- ws.connect();
1006
-
1007
- // Handle replay data - this is where historical records arrive
1008
- ws.onHistoricalData((coin, timestamp, data) => {
1009
- console.log(`${new Date(timestamp).toISOString()}: ${data.midPrice}`);
1010
- });
1011
-
1012
- // Replay lifecycle events
1013
- ws.onReplayStart((channel, coin, start, end, speed) => {
1014
- console.log(`Starting replay: ${channel}/${coin} at ${speed}x`);
1015
- });
1016
-
1017
- ws.onReplayComplete((channel, coin, recordsSent) => {
1018
- console.log(`Replay complete: ${recordsSent} records`);
1019
- });
1020
-
1021
- // Start replay at 10x speed
1022
- ws.replay('orderbook', 'BTC', {
1023
- start: Date.now() - 86400000, // 24 hours ago
1024
- end: Date.now(), // Optional, defaults to now
1025
- speed: 10 // Optional, defaults to 1x
1026
- });
1027
-
1028
- // Lighter.xyz replay with granularity (tier restrictions apply)
1029
- ws.replay('orderbook', 'BTC', {
1030
- start: Date.now() - 86400000,
1031
- speed: 10,
1032
- granularity: '10s' // Options: 'checkpoint', '30s', '10s', '1s', 'tick'
1033
- });
1034
-
1035
- // Handle tick-level data (granularity='tick', Enterprise tier)
1036
- ws.onHistoricalTickData((coin, checkpoint, deltas) => {
1037
- console.log(`Checkpoint: ${checkpoint.bids.length} bids`);
1038
- console.log(`Deltas: ${deltas.length} updates`);
1039
- // Apply deltas to checkpoint to reconstruct orderbook at any point
1040
- });
1041
-
1042
- // Control playback
1043
- ws.replayPause();
1044
- ws.replayResume();
1045
- ws.replaySeek(1704067200000); // Jump to timestamp
1046
- ws.replayStop();
1047
- ```
1048
-
1049
- ### Gap Detection
1050
-
1051
- During historical replay, the server automatically detects gaps in the data and notifies the client. This helps identify periods where data may be missing.
1052
-
1053
- ```typescript
1054
- // Handle gap notifications during replay
1055
- ws.onGap((channel, coin, gapStart, gapEnd, durationMinutes) => {
1056
- console.log(`Gap detected in ${channel}/${coin}:`);
1057
- console.log(` From: ${new Date(gapStart).toISOString()}`);
1058
- console.log(` To: ${new Date(gapEnd).toISOString()}`);
1059
- console.log(` Duration: ${durationMinutes} minutes`);
1060
- });
1061
-
1062
- // Start replay - gaps will be reported via onGap callback
1063
- ws.replay('orderbook', 'BTC', {
1064
- start: Date.now() - 86400000,
1065
- end: Date.now(),
1066
- speed: 10
1067
- });
1068
- ```
1069
-
1070
- Gap thresholds vary by channel:
1071
- - **orderbook**, **candles**, **liquidations**: 2 minutes
1072
- - **trades**: 60 minutes (trades can naturally have longer gaps during low activity periods)
1073
-
1074
- ### WebSocket Configuration
1075
-
1076
- ```typescript
1077
- const ws = new OxArchiveWs({
1078
- apiKey: '0xa_your_api_key', // Required
1079
- wsUrl: 'wss://api.0xarchive.io/ws', // Optional
1080
- autoReconnect: true, // Auto-reconnect on disconnect (default: true)
1081
- reconnectDelay: 1000, // Initial reconnect delay in ms (default: 1000)
1082
- maxReconnectAttempts: 10, // Max reconnect attempts (default: 10)
1083
- pingInterval: 30000, // Keep-alive ping interval in ms (default: 30000)
1084
- });
1085
- ```
1086
-
1087
- ### Available Channels
1088
-
1089
- #### Hyperliquid Channels
1090
-
1091
- | Channel | Description | Requires Coin | Historical Support |
1092
- |---------|-------------|---------------|-------------------|
1093
- | `orderbook` | L2 order book updates | Yes | Yes |
1094
- | `trades` | Trade/fill updates | Yes | Yes |
1095
- | `candles` | OHLCV candle data | Yes | Yes (replay only) |
1096
- | `liquidations` | Liquidation events (May 2025+) | Yes | Yes (replay only) |
1097
- | `open_interest` | Open interest snapshots | Yes | Replay/stream only |
1098
- | `funding` | Funding rate snapshots | Yes | Replay/stream only |
1099
- | `ticker` | Price and 24h volume | Yes | Real-time only |
1100
- | `all_tickers` | All market tickers | No | Real-time only |
1101
- | `l4_diffs` | L4 orderbook diffs with user attribution (Pro+) | Yes | Real-time only |
1102
- | `l4_orders` | Order lifecycle events with user attribution (Pro+) | Yes | Real-time only |
1103
-
1104
- #### HIP-3 Builder Perps Channels
1105
-
1106
- | Channel | Description | Requires Coin | Historical Support |
1107
- |---------|-------------|---------------|-------------------|
1108
- | `hip3_orderbook` | HIP-3 L2 order book snapshots | Yes | Yes |
1109
- | `hip3_trades` | HIP-3 trade/fill updates | Yes | Yes |
1110
- | `hip3_candles` | HIP-3 OHLCV candle data | Yes | Yes |
1111
- | `hip3_open_interest` | HIP-3 open interest snapshots | Yes | Replay/stream only |
1112
- | `hip3_funding` | HIP-3 funding rate snapshots | Yes | Replay/stream only |
1113
- | `hip3_liquidations` | HIP-3 liquidation events (Feb 2026+) | Yes | Yes (replay only) |
1114
- | `hip3_l4_diffs` | HIP-3 L4 orderbook diffs (Pro+) | Yes | Real-time only |
1115
- | `hip3_l4_orders` | HIP-3 order lifecycle events (Pro+) | Yes | Real-time only |
1116
-
1117
- > **Note:** HIP-3 coins are case-sensitive (e.g., `km:US500`, `xyz:XYZ100`). Do not uppercase them.
1118
-
1119
- #### Lighter.xyz Channels
1120
-
1121
- | Channel | Description | Requires Coin | Historical Support |
1122
- |---------|-------------|---------------|-------------------|
1123
- | `lighter_orderbook` | Lighter L2 order book (reconstructed) | Yes | Yes |
1124
- | `lighter_trades` | Lighter trade/fill updates | Yes | Yes |
1125
- | `lighter_candles` | Lighter OHLCV candle data | Yes | Yes |
1126
- | `lighter_open_interest` | Lighter open interest snapshots | Yes | Replay/stream only |
1127
- | `lighter_funding` | Lighter funding rate snapshots | Yes | Replay/stream only |
1128
- | `lighter_l3_orderbook` | Lighter L3 order-level orderbook (Pro+) | Yes | Yes |
1129
-
1130
- #### Candle Replay/Stream
1131
-
1132
- ```typescript
1133
- // Replay candles at 10x speed
1134
- ws.replay('candles', 'BTC', {
1135
- start: Date.now() - 86400000,
1136
- end: Date.now(),
1137
- speed: 10,
1138
- interval: '15m' // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
1139
- });
1140
-
1141
- // Lighter.xyz candles
1142
- ws.replay('lighter_candles', 'BTC', {
1143
- start: Date.now() - 86400000,
1144
- speed: 10,
1145
- interval: '5m'
1146
- });
1147
- ```
1148
-
1149
- #### HIP-3 Replay
1150
-
1151
- ```typescript
1152
- // Replay HIP-3 orderbook at 50x speed
1153
- ws.replay('hip3_orderbook', 'km:US500', {
1154
- start: Date.now() - 3600000,
1155
- end: Date.now(),
1156
- speed: 50,
1157
- });
1158
-
1159
- // HIP-3 candles
1160
- ws.replay('hip3_candles', 'km:US500', {
1161
- start: Date.now() - 86400000,
1162
- end: Date.now(),
1163
- speed: 100,
1164
- interval: '1h'
1165
- });
1166
- ```
1167
-
1168
- ### Multi-Channel Replay
1169
-
1170
- 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.
1171
-
1172
- ```typescript
1173
- const ws = new OxArchiveWs({ apiKey: 'ox_...' });
1174
- await ws.connect();
1175
-
1176
- // Handle initial snapshots (sent before timeline data)
1177
- ws.onReplaySnapshot((channel, coin, timestamp, data) => {
1178
- console.log(`Initial ${channel} state at ${new Date(timestamp).toISOString()}`);
1179
- if (channel === 'orderbook') {
1180
- currentOrderbook = data;
1181
- } else if (channel === 'funding') {
1182
- currentFundingRate = data;
1183
- } else if (channel === 'open_interest') {
1184
- currentOI = data;
1185
- }
1186
- });
1187
-
1188
- // Handle interleaved historical data
1189
- ws.onHistoricalData((coin, timestamp, data) => {
1190
- // The `channel` field on the raw message indicates which channel
1191
- // this data point belongs to
1192
- console.log(`${new Date(timestamp).toISOString()}: data received`);
1193
- });
1194
-
1195
- ws.onReplayComplete((channel, coin, count) => {
1196
- console.log(`Replay complete: ${count} records`);
1197
- });
1198
-
1199
- // Start multi-channel replay
1200
- ws.multiReplay(['orderbook', 'trades', 'funding'], 'BTC', {
1201
- start: Date.now() - 86400000,
1202
- end: Date.now(),
1203
- speed: 10
1204
- });
1205
-
1206
- // Playback controls work the same as single-channel
1207
- ws.replayPause();
1208
- ws.replayResume();
1209
- ws.replaySeek(1704067200000);
1210
- ws.replayStop();
1211
- ```
1212
-
1213
- **Channels available for multi-channel replay:** All historical channels can be combined in a single multi-channel replay. This includes `orderbook`, `trades`, `candles`, `liquidations`, `open_interest`, `funding`, and their `lighter_*` and `hip3_*` variants.
1214
-
1215
- ### WebSocket Connection States
1216
-
1217
- ```typescript
1218
- ws.getState(); // 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
1219
- ws.isConnected(); // boolean
1220
- ```
1221
-
1222
- ## Timestamp Formats
1223
-
1224
- The SDK accepts timestamps as Unix milliseconds or Date objects:
1225
-
1226
- ```typescript
1227
- // Unix milliseconds (recommended)
1228
- client.hyperliquid.orderbook.history('BTC', {
1229
- start: Date.now() - 86400000,
1230
- end: Date.now()
1231
- });
1232
-
1233
- // Date objects (converted automatically)
1234
- client.hyperliquid.orderbook.history('BTC', {
1235
- start: new Date('2024-01-01'),
1236
- end: new Date('2024-01-02')
1237
- });
1238
-
1239
- // WebSocket replay also accepts both
1240
- ws.replay('orderbook', 'BTC', {
1241
- start: Date.now() - 3600000,
1242
- end: Date.now(),
1243
- speed: 10
1244
- });
1245
- ```
1246
-
1247
- ## Error Handling
1248
-
1249
- ```typescript
1250
- import { OxArchive, OxArchiveError } from '@0xarchive/sdk';
1251
-
1252
- try {
1253
- const orderbook = await client.orderbook.get('INVALID');
1254
- } catch (error) {
1255
- if (error instanceof OxArchiveError) {
1256
- console.error(`API Error: ${error.message}`);
1257
- console.error(`Status Code: ${error.code}`);
1258
- console.error(`Request ID: ${error.requestId}`);
1259
- }
1260
- }
1261
- ```
1262
-
1263
- ## TypeScript Support
1264
-
1265
- Full TypeScript support with exported types:
1266
-
1267
- ```typescript
1268
- import type {
1269
- OrderBook,
1270
- PriceLevel,
1271
- Trade,
1272
- Candle,
1273
- Instrument,
1274
- LighterInstrument,
1275
- Hip3Instrument,
1276
- LighterGranularity,
1277
- FundingRate,
1278
- OpenInterest,
1279
- Liquidation,
1280
- LiquidationVolume,
1281
- CoinFreshness,
1282
- CoinSummary,
1283
- PriceSnapshot,
1284
- CursorResponse,
1285
- WsOptions,
1286
- WsChannel,
1287
- WsConnectionState,
1288
- WsReplaySnapshot,
1289
- // Orderbook reconstruction (Enterprise)
1290
- OrderbookDelta,
1291
- TickData,
1292
- ReconstructedOrderBook,
1293
- ReconstructOptions,
1294
- TickHistoryParams,
1295
- } from '@0xarchive/sdk';
1296
-
1297
- // Import reconstructor class
1298
- import { OrderBookReconstructor } from '@0xarchive/sdk';
1299
- ```
1300
-
1301
- ## Runtime Validation
1302
-
1303
- Enable Zod schema validation for API responses:
1304
-
1305
- ```typescript
1306
- const client = new OxArchive({
1307
- apiKey: '0xa_your_api_key',
1308
- validate: true // Enable runtime validation
1309
- });
1310
- ```
1311
-
1312
- When enabled, responses are validated against Zod schemas and throw `OxArchiveError` with status 422 if validation fails.
1313
-
1314
- ## Bulk Data Downloads
1315
-
1316
- For large-scale data exports (full order books, complete trade history, etc.), use the S3 Parquet bulk export available at [0xarchive.io/data](https://0xarchive.io/data). The Data Explorer lets you select time ranges, symbols, and data types, then download compressed Parquet files directly.
1317
-
1318
- ## Requirements
1319
-
1320
- - Node.js 18+ or modern browsers with `fetch` and `WebSocket` support
1321
-
1322
- ## License
1323
-
1324
- MIT
1
+ # @0xarchive/sdk
2
+
3
+ TypeScript client for 0xArchive market data in Node services, dashboards, coding-agent workflows, and agent backends.
4
+
5
+ 0xArchive is granular market data infrastructure for Hyperliquid and Lighter.xyz. HIP-3 builder perps live under the Hyperliquid namespace at `/v1/hyperliquid/hip3` and `client.hyperliquid.hip3`. HIP-4 binary outcome markets live at `/v1/hyperliquid/hip4` and `client.hyperliquid.hip4`. Hyperliquid Spot lives at `/v1/hyperliquid/spot` and `client.spot`.
6
+
7
+ 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.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @0xarchive/sdk
13
+ # or
14
+ yarn add @0xarchive/sdk
15
+ # or
16
+ pnpm add @0xarchive/sdk
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { OxArchive } from '@0xarchive/sdk';
23
+
24
+ const client = new OxArchive({ apiKey: '0xa_your_api_key' });
25
+
26
+ // First successful call: Hyperliquid BTC order book
27
+ const hlOrderbook = await client.hyperliquid.orderbook.get('BTC');
28
+ console.log(`Hyperliquid BTC mid price: ${hlOrderbook.midPrice}`);
29
+
30
+ // Lighter.xyz uses its own venue client
31
+ const lighterOrderbook = await client.lighter.orderbook.get('BTC');
32
+ console.log(`Lighter BTC mid price: ${lighterOrderbook.midPrice}`);
33
+
34
+ // Hyperliquid HIP-3 builder perps stay under client.hyperliquid.hip3
35
+ const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
36
+ const hip3Orderbook = await client.hyperliquid.hip3.orderbook.get('km:US500');
37
+ const hip3Trades = await client.hyperliquid.hip3.trades.recent('km:US500');
38
+ const hip3Funding = await client.hyperliquid.hip3.funding.current('xyz:XYZ100');
39
+ const hip3Oi = await client.hyperliquid.hip3.openInterest.current('xyz:XYZ100');
40
+
41
+ // Hyperliquid Spot lives at client.spot. Symbols are dashed canonical (HYPE-USDC).
42
+ const spotPairs = await client.spot.pairs.list();
43
+ const spotOrderbook = await client.spot.orderbook.get('HYPE-USDC');
44
+ const spotTrades = await client.spot.trades.recent('HYPE-USDC');
45
+
46
+ // Get historical order book snapshots
47
+ const history = await client.hyperliquid.orderbook.history('ETH', {
48
+ start: Date.now() - 86400000, // 24 hours ago
49
+ end: Date.now(),
50
+ limit: 100
51
+ });
52
+ ```
53
+
54
+ ## Choose Your Next Path
55
+
56
+ | Need | Link |
57
+ | --- | --- |
58
+ | First authenticated route | [Quick Start](https://www.0xarchive.io/docs/quick-start) |
59
+ | SDK install and route docs | [SDK docs](https://www.0xarchive.io/docs/sdks) |
60
+ | Claude Code, GPT Codex, and coding-agent workflows | [AI Clients](https://www.0xarchive.io/docs/ai-clients) |
61
+ | Example notebooks | [Examples](https://github.com/0xArchiveIO/examples) |
62
+ | File-based historical pulls | [Data Catalog](https://www.0xarchive.io/data) |
63
+ | Route contract and machine context | [OpenAPI](https://www.0xarchive.io/openapi.json), [llms.txt](https://www.0xarchive.io/llms.txt) |
64
+
65
+ ## Data Coverage
66
+
67
+ | Venue | Coverage | Notes |
68
+ | --- | --- | --- |
69
+ | Hyperliquid | April 2023+ | Perpetuals across the full venue |
70
+ | Hyperliquid HIP-3 | February 2026+ | Free tier: `km:US500`. Build+: all HIP-3 symbols. Pro+: orderbook history. |
71
+ | Hyperliquid HIP-4 | March 2026+ | Outcome markets. Pro+ for orderbook + L4 + orders. |
72
+ | Hyperliquid Spot | March 2025+ for trades; May 2026+ for orderbook, L4, TWAP statuses | 294 dashed pairs (`HYPE-USDC`, `PURR-USDC`). No funding, OI, liquidations, or candles (perp-only constructs). |
73
+ | Lighter.xyz | August 2025+ for fills; January 2026+ for orderbooks, open interest, funding rates | Perpetuals |
74
+
75
+ ## Configuration
76
+
77
+ ```typescript
78
+ const client = new OxArchive({
79
+ apiKey: '0xa_your_api_key', // Required
80
+ baseUrl: 'https://api.0xarchive.io', // Optional
81
+ timeout: 30000, // Optional, request timeout in ms (default: 30000)
82
+ validate: false, // Optional, enable Zod schema validation
83
+ });
84
+ ```
85
+
86
+ ## REST API Reference
87
+
88
+ Core resources (orderbook, trades, instruments, funding, openInterest, candles, freshness, summary, priceHistory) are available on both `client.hyperliquid.*` and `client.lighter.*`. Some resources are exchange-specific -- see each section for details.
89
+
90
+ ### Order Book
91
+
92
+ ```typescript
93
+ // Get current order book (Hyperliquid)
94
+ const orderbook = await client.hyperliquid.orderbook.get('BTC');
95
+
96
+ // Get current order book (Lighter.xyz)
97
+ const lighterOb = await client.lighter.orderbook.get('BTC');
98
+
99
+ // Get order book at specific timestamp with custom depth
100
+ const historical = await client.hyperliquid.orderbook.get('BTC', {
101
+ timestamp: 1704067200000,
102
+ depth: 20 // Number of levels per side
103
+ });
104
+
105
+ // Get historical snapshots (start is required)
106
+ const history = await client.hyperliquid.orderbook.history('BTC', {
107
+ start: Date.now() - 86400000,
108
+ end: Date.now(),
109
+ limit: 1000
110
+ });
111
+ ```
112
+
113
+ #### Orderbook Depth Limits
114
+
115
+ The `depth` parameter controls how many price levels are returned per side. Tier-based limits apply:
116
+
117
+ | Tier | Max Depth |
118
+ |------|-----------|
119
+ | Free | 20 |
120
+ | Build | 200 |
121
+ | Pro | Full Depth |
122
+ | Enterprise | Full Depth |
123
+
124
+ **Note:** Hyperliquid L2 source data contains 20 levels. Full-depth L2 (derived from L4) and Lighter.xyz provide full depth on Pro+. Depth limits apply to L2 snapshot endpoints only — L4 and L2 diff endpoints return full data.
125
+
126
+ #### Lighter Orderbook Granularity
127
+
128
+ Lighter.xyz orderbook history supports a `granularity` parameter for different data resolutions. Tier restrictions apply.
129
+
130
+ | Granularity | Interval | Tier Required | Credit Multiplier |
131
+ |-------------|----------|---------------|-------------------|
132
+ | `checkpoint` | ~60s | Free+ | 1x |
133
+ | `30s` | 30s | Build+ | 2x |
134
+ | `10s` | 10s | Build+ | 3x |
135
+ | `1s` | 1s | Pro+ | 10x |
136
+ | `tick` | tick-level | Enterprise | 20x |
137
+
138
+ ```typescript
139
+ // Get Lighter orderbook history with 10s resolution (Build+ tier)
140
+ const history = await client.lighter.orderbook.history('BTC', {
141
+ start: Date.now() - 86400000,
142
+ end: Date.now(),
143
+ granularity: '10s'
144
+ });
145
+
146
+ // Get 1-second resolution (Pro+ tier)
147
+ const history = await client.lighter.orderbook.history('BTC', {
148
+ start: Date.now() - 86400000,
149
+ end: Date.now(),
150
+ granularity: '1s'
151
+ });
152
+
153
+ // Tick-level data (Enterprise tier) - returns checkpoint + raw deltas
154
+ const history = await client.lighter.orderbook.history('BTC', {
155
+ start: Date.now() - 86400000,
156
+ end: Date.now(),
157
+ granularity: 'tick'
158
+ });
159
+ ```
160
+
161
+ **Note:** The `granularity` parameter is ignored for Hyperliquid orderbook history.
162
+
163
+ #### Orderbook Reconstruction (Enterprise Tier)
164
+
165
+ For tick-level data, the SDK provides client-side orderbook reconstruction. This efficiently reconstructs full orderbook state from a checkpoint and incremental deltas.
166
+
167
+ ```typescript
168
+ import { OrderBookReconstructor } from '@0xarchive/sdk';
169
+
170
+ // Option 1: Get fully reconstructed snapshots (simplest)
171
+ const snapshots = await client.lighter.orderbook.historyReconstructed('BTC', {
172
+ start: Date.now() - 3600000,
173
+ end: Date.now()
174
+ });
175
+
176
+ for (const ob of snapshots) {
177
+ console.log(`${ob.timestamp}: bid=${ob.bids[0]?.px} ask=${ob.asks[0]?.px}`);
178
+ }
179
+
180
+ // Option 2: Get raw tick data for custom reconstruction
181
+ const tickData = await client.lighter.orderbook.historyTick('BTC', {
182
+ start: Date.now() - 3600000,
183
+ end: Date.now()
184
+ });
185
+
186
+ console.log(`Checkpoint: ${tickData.checkpoint.bids.length} bids`);
187
+ console.log(`Deltas: ${tickData.deltas.length} updates`);
188
+
189
+ // Option 3: Auto-paginating iterator (recommended for large time ranges)
190
+ // Automatically handles pagination, fetching up to 1,000 deltas per request
191
+ for await (const snapshot of client.lighter.orderbook.iterateTickHistory('BTC', {
192
+ start: Date.now() - 86400000, // 24 hours of data
193
+ end: Date.now()
194
+ })) {
195
+ console.log(snapshot.timestamp, 'Mid:', snapshot.midPrice);
196
+ if (someCondition(snapshot)) break; // Early exit supported
197
+ }
198
+
199
+ // Option 4: Manual iteration (single page, for custom logic)
200
+ const reconstructor = client.lighter.orderbook.createReconstructor();
201
+ for (const snapshot of reconstructor.iterate(tickData.checkpoint, tickData.deltas)) {
202
+ // Process each snapshot without loading all into memory
203
+ if (someCondition(snapshot)) break; // Early exit if needed
204
+ }
205
+
206
+ // Option 5: Get only final state (most efficient)
207
+ const final = reconstructor.reconstructFinal(tickData.checkpoint, tickData.deltas);
208
+
209
+ // Check for sequence gaps
210
+ const gaps = OrderBookReconstructor.detectGaps(tickData.deltas);
211
+ if (gaps.length > 0) {
212
+ console.warn('Sequence gaps detected:', gaps);
213
+ }
214
+ ```
215
+
216
+ **Methods:**
217
+ | Method | Description |
218
+ |--------|-------------|
219
+ | `historyTick(coin, params)` | Get raw checkpoint + deltas (single page, max 1,000 deltas) |
220
+ | `historyReconstructed(coin, params, options)` | Get fully reconstructed snapshots (single page) |
221
+ | `iterateTickHistory(coin, params, depth?)` | Auto-paginating async iterator for large time ranges |
222
+ | `createReconstructor()` | Create a reconstructor instance for manual control |
223
+
224
+ **Note:** The API returns a maximum of 1,000 deltas per request. For time ranges with more deltas, use `iterateTickHistory()` which handles pagination automatically.
225
+
226
+ **ReconstructOptions:**
227
+ | Option | Default | Description |
228
+ |--------|---------|-------------|
229
+ | `depth` | all | Maximum price levels in output |
230
+ | `emitAll` | `true` | If `false`, only return final state |
231
+
232
+ ### Trades
233
+
234
+ The trades API uses cursor-based pagination for efficient retrieval of large datasets.
235
+
236
+ ```typescript
237
+ // Get trade history with cursor-based pagination
238
+ let result = await client.hyperliquid.trades.list('BTC', {
239
+ start: Date.now() - 86400000,
240
+ end: Date.now(),
241
+ limit: 1000
242
+ });
243
+
244
+ // Paginate through all results
245
+ const allTrades = [...result.data];
246
+ while (result.nextCursor) {
247
+ result = await client.hyperliquid.trades.list('BTC', {
248
+ start: Date.now() - 86400000,
249
+ end: Date.now(),
250
+ cursor: result.nextCursor,
251
+ limit: 1000
252
+ });
253
+ allTrades.push(...result.data);
254
+ }
255
+
256
+ // Get recent trades (Lighter only - has real-time data)
257
+ const recent = await client.lighter.trades.recent('BTC', 100);
258
+ ```
259
+
260
+ **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.
261
+
262
+ ### Instruments
263
+
264
+ ```typescript
265
+ // List all trading instruments (Hyperliquid)
266
+ const instruments = await client.hyperliquid.instruments.list();
267
+
268
+ // Get specific instrument details
269
+ const btc = await client.hyperliquid.instruments.get('BTC');
270
+ console.log(`BTC size decimals: ${btc.szDecimals}`);
271
+ ```
272
+
273
+ #### Lighter.xyz Instruments
274
+
275
+ Lighter instruments have a different schema with additional fields for fees, market IDs, and minimum order amounts:
276
+
277
+ ```typescript
278
+ // List Lighter instruments (returns LighterInstrument, not Instrument)
279
+ const lighterInstruments = await client.lighter.instruments.list();
280
+
281
+ // Get specific Lighter instrument
282
+ const eth = await client.lighter.instruments.get('ETH');
283
+ console.log(`ETH taker fee: ${eth.takerFee}`);
284
+ console.log(`ETH maker fee: ${eth.makerFee}`);
285
+ console.log(`ETH market ID: ${eth.marketId}`);
286
+ console.log(`ETH min base amount: ${eth.minBaseAmount}`);
287
+ ```
288
+
289
+ **Key differences:**
290
+ | Field | Hyperliquid (`Instrument`) | Lighter (`LighterInstrument`) |
291
+ |-------|---------------------------|------------------------------|
292
+ | Symbol | `name` | `symbol` |
293
+ | Size decimals | `szDecimals` | `sizeDecimals` |
294
+ | Fee info | Not available | `takerFee`, `makerFee`, `liquidationFee` |
295
+ | Market ID | Not available | `marketId` |
296
+ | Min amounts | Not available | `minBaseAmount`, `minQuoteAmount` |
297
+
298
+ #### HIP-3 Instruments
299
+
300
+ HIP-3 instruments are derived from live market data and include mark price, open interest, and mid price:
301
+
302
+ ```typescript
303
+ // List all HIP-3 instruments (no tier restriction)
304
+ const hip3Instruments = await client.hyperliquid.hip3.instruments.list();
305
+ for (const inst of hip3Instruments) {
306
+ console.log(`${inst.coin} (${inst.namespace}:${inst.ticker}): mark=${inst.markPrice}, OI=${inst.openInterest}`);
307
+ }
308
+
309
+ // Get specific HIP-3 instrument (case-sensitive)
310
+ const us500 = await client.hyperliquid.hip3.instruments.get('km:US500');
311
+ console.log(`Mark price: ${us500.markPrice}`);
312
+ ```
313
+
314
+ **Available HIP-3 Coins:**
315
+ | Builder | Coins |
316
+ |---------|-------|
317
+ | xyz (Hyperliquid) | `xyz:XYZ100` |
318
+ | km (Kinetiq Markets) | `km:US500`, `km:SMALL2000`, `km:GOOGL`, `km:USBOND`, `km:GOLD`, `km:USTECH`, `km:NVDA`, `km:SILVER`, `km:BABA` |
319
+
320
+ #### HIP-4 Outcome Markets
321
+
322
+ 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, no liquidations, and no candles by design. `mark_price` and `midPrice` are implied probabilities in `[0, 1]`, not USD prices.
323
+
324
+ **Path encoding:** the backend accepts both the bare numeric form (`'0'`, `'1'`, ...) and the on-chain `#`-prefixed form (`'#0'`, `'#1'`, ...) — and `#`-prefixed is the canonical form returned by the API in `coin` fields. The SDK URL-encodes the value on the wire (`#` becomes `%23`) so the `#` form survives `fetch` (the WHATWG `URL` parser would otherwise treat `#` as a fragment delimiter and silently drop the rest of the path). Both forms are equivalent at the API; pass whichever is convenient.
325
+
326
+ ```typescript
327
+ // Per-side instruments (one row per #N coin)
328
+ const sides = await client.hyperliquid.hip4.instruments.list();
329
+ for (const s of sides) {
330
+ console.log(`${s.symbol} (outcome ${s.outcomeId}/${s.side}): ${s.displayTitle}`);
331
+ console.log(` slug: ${s.slug}, settled: ${s.isSettled}`);
332
+ }
333
+
334
+ // Per-outcome aggregates (one row per outcome)
335
+ const outcomes = await client.hyperliquid.hip4.listOutcomes({ isSettled: false, limit: 50 });
336
+ for (const o of outcomes.data) {
337
+ console.log(`outcome ${o.outcomeId}: ${o.displayTitle}`);
338
+ console.log(` pair: ${o.outcomePair?.[0]} / ${o.outcomePair?.[1]}`);
339
+ console.log(` expiry: ${o.expiry}, target: ${o.targetPrice}`);
340
+ }
341
+
342
+ // Detail (includes aggregatedOi)
343
+ const detail = await client.hyperliquid.hip4.getOutcome(0);
344
+ console.log(detail.aggregatedOi?.outcomeDisplayOpenInterestContracts);
345
+
346
+ // Slug-based lookup (per-outcome OR per-side slug)
347
+ const bySlug = await client.hyperliquid.hip4.getOutcomeBySlug('btc-above-78213-may-04-0600');
348
+
349
+ // Slug-filter on listOutcomes
350
+ const filtered = await client.hyperliquid.hip4.listOutcomes({
351
+ slug: 'btc-above-78213-may-04-0600',
352
+ });
353
+
354
+ // Orderbook (Pro+). Bare numeric form is recommended.
355
+ const ob = await client.hyperliquid.hip4.getOrderbook('0');
356
+ // ob.midPrice is a probability ∈ [0, 1] — implied YES probability for #0.
357
+
358
+ // Trades, OI, and prices
359
+ const trades = await client.hyperliquid.hip4.getTradesRecent('0', 50);
360
+ const oiNow = await client.hyperliquid.hip4.getOpenInterestCurrent('0');
361
+ const prices = await client.hyperliquid.hip4.getPrices('0', {
362
+ start: Date.now() - 86400000,
363
+ end: Date.now(),
364
+ });
365
+
366
+ // Convenience
367
+ const summary = await client.hyperliquid.hip4.getSummary('0');
368
+ const fresh = await client.hyperliquid.hip4.getFreshness('0');
369
+
370
+ // Pro+: L4
371
+ const l4 = await client.hyperliquid.hip4.getL4Orderbook('0');
372
+ const orders = await client.hyperliquid.hip4.getOrderHistory('0', {
373
+ start: Date.now() - 3600000,
374
+ end: Date.now(),
375
+ });
376
+ ```
377
+
378
+ > **No HIP-4 funding, liquidations, or candles.** These methods do not exist on `client.hyperliquid.hip4` by design. Don't expect them.
379
+
380
+ #### Hyperliquid Spot
381
+
382
+ 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.
383
+
384
+ Spot has **no funding, no open interest, no liquidations, and no candles** by design. Those are perpetual constructs. The SDK omits those resources from the spot client.
385
+
386
+ ```typescript
387
+ // Pairs (one row per dashed symbol)
388
+ const pairs = await client.spot.pairs.list();
389
+ const hype = await client.spot.pairs.get('HYPE-USDC');
390
+ console.log(`${hype.symbol}: mark=${hype.markPrice}, mid=${hype.midPrice}`);
391
+
392
+ // Orderbook (Build+, live from 2026-05-05)
393
+ const ob = await client.spot.orderbook.get('HYPE-USDC');
394
+ console.log(`${ob.coin} mid: ${ob.midPrice}`);
395
+
396
+ // Orderbook history
397
+ const obHistory = await client.spot.orderbook.history('HYPE-USDC', {
398
+ start: Date.now() - 3600000,
399
+ end: Date.now(),
400
+ limit: 100,
401
+ });
402
+
403
+ // Trade history (S3 backfill from 2025-03-22)
404
+ const trades = await client.spot.trades.list('HYPE-USDC', {
405
+ start: Date.now() - 86400000,
406
+ end: Date.now(),
407
+ limit: 1000,
408
+ });
409
+
410
+ // Recent trades (real-time)
411
+ const recent = await client.spot.trades.recent('HYPE-USDC', 100);
412
+
413
+ // L4 reconstruction (Pro+, live from 2026-05-05)
414
+ const l4 = await client.spot.l4Orderbook.get('HYPE-USDC');
415
+ const diffs = await client.spot.l4Orderbook.diffs('HYPE-USDC', {
416
+ start: Date.now() - 3600000,
417
+ end: Date.now(),
418
+ });
419
+
420
+ // Order lifecycle events (Pro+)
421
+ const orders = await client.spot.orders.history('HYPE-USDC', {
422
+ start: Date.now() - 86400000,
423
+ end: Date.now(),
424
+ });
425
+
426
+ // TWAP statuses by symbol or by user wallet
427
+ const bySymbol = await client.spot.twap.bySymbol('HYPE-USDC', {
428
+ start: Date.now() - 86400000,
429
+ end: Date.now(),
430
+ });
431
+ const byUser = await client.spot.twap.byUser('0xabc...', {
432
+ start: Date.now() - 86400000,
433
+ end: Date.now(),
434
+ });
435
+
436
+ // Per-symbol freshness across all spot data types
437
+ const fresh = await client.spot.freshness('HYPE-USDC');
438
+ console.log(`Orderbook last updated: ${fresh.orderbook.lastUpdated}`);
439
+ ```
440
+
441
+ > **Coverage caveats.** 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.
442
+
443
+ ### Funding Rates
444
+
445
+ ```typescript
446
+ // Get current funding rate
447
+ const current = await client.hyperliquid.funding.current('BTC');
448
+
449
+ // Get funding rate history (start is required)
450
+ const history = await client.hyperliquid.funding.history('ETH', {
451
+ start: Date.now() - 86400000 * 7,
452
+ end: Date.now()
453
+ });
454
+
455
+ // Get funding rate history with aggregation interval
456
+ const hourly = await client.hyperliquid.funding.history('BTC', {
457
+ start: Date.now() - 86400000 * 7,
458
+ end: Date.now(),
459
+ interval: '1h'
460
+ });
461
+ ```
462
+
463
+ #### Funding History Parameters
464
+
465
+ | Parameter | Type | Required | Description |
466
+ |-----------|------|----------|-------------|
467
+ | `start` | `number \| string` | Yes | Start timestamp (Unix ms or ISO string) |
468
+ | `end` | `number \| string` | Yes | End timestamp (Unix ms or ISO string) |
469
+ | `cursor` | `number \| string` | No | Cursor from previous response for pagination |
470
+ | `limit` | `number` | No | Max results (default: 100, max: 1000) |
471
+ | `interval` | `OiFundingInterval` | No | Aggregation interval: `'5m'`, `'15m'`, `'30m'`, `'1h'`, `'4h'`, `'1d'`. When omitted, raw ~1 min data is returned. |
472
+
473
+ ### Open Interest
474
+
475
+ ```typescript
476
+ // Get current open interest
477
+ const current = await client.hyperliquid.openInterest.current('BTC');
478
+
479
+ // Get open interest history (start is required)
480
+ const history = await client.hyperliquid.openInterest.history('ETH', {
481
+ start: Date.now() - 86400000,
482
+ end: Date.now(),
483
+ limit: 100
484
+ });
485
+
486
+ // Get open interest history with aggregation interval
487
+ const hourly = await client.hyperliquid.openInterest.history('BTC', {
488
+ start: Date.now() - 86400000,
489
+ end: Date.now(),
490
+ interval: '1h'
491
+ });
492
+ ```
493
+
494
+ #### Open Interest History Parameters
495
+
496
+ | Parameter | Type | Required | Description |
497
+ |-----------|------|----------|-------------|
498
+ | `start` | `number \| string` | Yes | Start timestamp (Unix ms or ISO string) |
499
+ | `end` | `number \| string` | Yes | End timestamp (Unix ms or ISO string) |
500
+ | `cursor` | `number \| string` | No | Cursor from previous response for pagination |
501
+ | `limit` | `number` | No | Max results (default: 100, max: 1000) |
502
+ | `interval` | `OiFundingInterval` | No | Aggregation interval: `'5m'`, `'15m'`, `'30m'`, `'1h'`, `'4h'`, `'1d'`. When omitted, raw ~1 min data is returned. |
503
+
504
+ ### Liquidations
505
+
506
+ Get historical liquidation events. Data available from May 2025 onwards for Hyperliquid, and from February 2026 for HIP-3.
507
+
508
+ ```typescript
509
+ // Get liquidation history for a coin (Hyperliquid)
510
+ const liquidations = await client.hyperliquid.liquidations.history('BTC', {
511
+ start: Date.now() - 86400000,
512
+ end: Date.now(),
513
+ limit: 100
514
+ });
515
+
516
+ // Paginate through all results
517
+ const allLiquidations = [...liquidations.data];
518
+ while (liquidations.nextCursor) {
519
+ const next = await client.hyperliquid.liquidations.history('BTC', {
520
+ start: Date.now() - 86400000,
521
+ end: Date.now(),
522
+ cursor: liquidations.nextCursor,
523
+ limit: 1000
524
+ });
525
+ allLiquidations.push(...next.data);
526
+ }
527
+
528
+ // Get liquidations for a specific user
529
+ const userLiquidations = await client.hyperliquid.liquidations.byUser('0x1234...', {
530
+ start: Date.now() - 86400000 * 7,
531
+ end: Date.now(),
532
+ coin: 'BTC' // optional filter
533
+ });
534
+
535
+ // HIP-3 liquidations (case-sensitive coins)
536
+ const hip3Liquidations = await client.hyperliquid.hip3.liquidations.history('km:US500', {
537
+ start: Date.now() - 86400000,
538
+ end: Date.now(),
539
+ limit: 100
540
+ });
541
+ ```
542
+
543
+ ### Liquidation Volume
544
+
545
+ 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.
546
+
547
+ ```typescript
548
+ // Get hourly liquidation volume for the last week (Hyperliquid)
549
+ const volume = await client.hyperliquid.liquidations.volume('BTC', {
550
+ start: Date.now() - 86400000 * 7,
551
+ end: Date.now(),
552
+ interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
553
+ });
554
+
555
+ for (const bucket of volume.data) {
556
+ console.log(`${bucket.timestamp}: total=$${bucket.totalUsd}, long=$${bucket.longUsd}, short=$${bucket.shortUsd}`);
557
+ }
558
+
559
+ // HIP-3 liquidation volume (case-sensitive coins)
560
+ const hip3Volume = await client.hyperliquid.hip3.liquidations.volume('km:US500', {
561
+ start: Date.now() - 86400000 * 7,
562
+ end: Date.now(),
563
+ interval: '1h'
564
+ });
565
+ ```
566
+
567
+ ### Orders
568
+
569
+ Access order history, order flow aggregations, and TP/SL (take-profit/stop-loss) orders. Available for Hyperliquid and HIP-3.
570
+
571
+ ```typescript
572
+ // Get order history for a coin
573
+ const orders = await client.hyperliquid.orders.history('BTC', {
574
+ start: Date.now() - 86400000,
575
+ end: Date.now(),
576
+ limit: 1000,
577
+ user: '0x1234...', // optional: filter by user address
578
+ status: 'filled', // optional: filter by status
579
+ order_type: 'limit', // optional: filter by order type
580
+ });
581
+
582
+ // Paginate through all results
583
+ const allOrders = [...orders.data];
584
+ while (orders.nextCursor) {
585
+ const next = await client.hyperliquid.orders.history('BTC', {
586
+ start: Date.now() - 86400000,
587
+ end: Date.now(),
588
+ cursor: orders.nextCursor,
589
+ limit: 1000
590
+ });
591
+ allOrders.push(...next.data);
592
+ }
593
+
594
+ // Get order flow (aggregated order activity over time)
595
+ const flow = await client.hyperliquid.orders.flow('BTC', {
596
+ start: Date.now() - 86400000,
597
+ end: Date.now(),
598
+ interval: '1h', // optional aggregation interval
599
+ limit: 100
600
+ });
601
+
602
+ // Get TP/SL orders
603
+ const tpsl = await client.hyperliquid.orders.tpsl('BTC', {
604
+ start: Date.now() - 86400000,
605
+ end: Date.now(),
606
+ user: '0x1234...', // optional: filter by user
607
+ triggered: true, // optional: filter by triggered status
608
+ });
609
+
610
+ // HIP-3 orders (case-sensitive coins)
611
+ const hip3Orders = await client.hyperliquid.hip3.orders.history('km:US500', {
612
+ start: Date.now() - 86400000,
613
+ end: Date.now(),
614
+ limit: 1000
615
+ });
616
+
617
+ const hip3Flow = await client.hyperliquid.hip3.orders.flow('km:US500', {
618
+ start: Date.now() - 86400000,
619
+ end: Date.now(),
620
+ interval: '1h'
621
+ });
622
+
623
+ const hip3Tpsl = await client.hyperliquid.hip3.orders.tpsl('km:US500', {
624
+ start: Date.now() - 86400000,
625
+ end: Date.now()
626
+ });
627
+ ```
628
+
629
+ ### L4 Order Book
630
+
631
+ Access L4 orderbook snapshots, diffs, and history. L4 data includes user attribution (who placed each order). Available for Hyperliquid and HIP-3.
632
+
633
+ ```typescript
634
+ // Get current L4 orderbook snapshot
635
+ const l4Ob = await client.hyperliquid.l4Orderbook.get('BTC');
636
+
637
+ // Get L4 orderbook at a specific timestamp with custom depth
638
+ const l4Historical = await client.hyperliquid.l4Orderbook.get('BTC', {
639
+ timestamp: 1704067200000,
640
+ depth: 20
641
+ });
642
+
643
+ // Get L4 orderbook diffs (incremental updates)
644
+ const diffs = await client.hyperliquid.l4Orderbook.diffs('BTC', {
645
+ start: Date.now() - 3600000,
646
+ end: Date.now(),
647
+ limit: 1000
648
+ });
649
+
650
+ // Paginate through diffs
651
+ const allDiffs = [...diffs.data];
652
+ while (diffs.nextCursor) {
653
+ const next = await client.hyperliquid.l4Orderbook.diffs('BTC', {
654
+ start: Date.now() - 3600000,
655
+ end: Date.now(),
656
+ cursor: diffs.nextCursor,
657
+ limit: 1000
658
+ });
659
+ allDiffs.push(...next.data);
660
+ }
661
+
662
+ // Get L4 orderbook history (full snapshots over time)
663
+ const l4History = await client.hyperliquid.l4Orderbook.history('BTC', {
664
+ start: Date.now() - 86400000,
665
+ end: Date.now(),
666
+ limit: 1000
667
+ });
668
+
669
+ // HIP-3 L4 orderbook (case-sensitive coins)
670
+ const hip3L4 = await client.hyperliquid.hip3.l4Orderbook.get('km:US500');
671
+
672
+ const hip3L4Diffs = await client.hyperliquid.hip3.l4Orderbook.diffs('km:US500', {
673
+ start: Date.now() - 3600000,
674
+ end: Date.now(),
675
+ limit: 1000
676
+ });
677
+
678
+ const hip3L4History = await client.hyperliquid.hip3.l4Orderbook.history('km:US500', {
679
+ start: Date.now() - 86400000,
680
+ end: Date.now(),
681
+ limit: 1000
682
+ });
683
+ ```
684
+
685
+ ### L3 Order Book (Lighter only)
686
+
687
+ Access L3 orderbook snapshots and history from Lighter.xyz. L3 data includes individual order-level detail.
688
+
689
+ ```typescript
690
+ // Get current L3 orderbook
691
+ const l3Ob = await client.lighter.l3Orderbook.get('BTC');
692
+
693
+ // Get L3 orderbook at a specific timestamp with custom depth
694
+ const l3Historical = await client.lighter.l3Orderbook.get('BTC', {
695
+ timestamp: 1704067200000,
696
+ depth: 20
697
+ });
698
+
699
+ // Get L3 orderbook history
700
+ const l3History = await client.lighter.l3Orderbook.history('BTC', {
701
+ start: Date.now() - 86400000,
702
+ end: Date.now(),
703
+ limit: 1000
704
+ });
705
+
706
+ // Paginate through L3 history
707
+ const allL3 = [...l3History.data];
708
+ while (l3History.nextCursor) {
709
+ const next = await client.lighter.l3Orderbook.history('BTC', {
710
+ start: Date.now() - 86400000,
711
+ end: Date.now(),
712
+ cursor: l3History.nextCursor,
713
+ limit: 1000
714
+ });
715
+ allL3.push(...next.data);
716
+ }
717
+ ```
718
+
719
+ ### L2 Order Book (Full-Depth)
720
+
721
+ Access L2 full-depth orderbook derived from L4 data. Available for Hyperliquid and HIP-3.
722
+
723
+ ```typescript
724
+ // L2 full-depth orderbook (Build+ tier)
725
+ const l2 = await client.hyperliquid.l2Orderbook.get('BTC');
726
+
727
+ // L2 orderbook at a specific timestamp with depth
728
+ const l2Historical = await client.hyperliquid.l2Orderbook.get('BTC', {
729
+ timestamp: 1704067200000,
730
+ depth: 50
731
+ });
732
+
733
+ // L2 orderbook history (Build+ tier)
734
+ const l2History = await client.hyperliquid.l2Orderbook.history('BTC', {
735
+ start: Date.now() - 86400000,
736
+ end: Date.now(),
737
+ limit: 1000
738
+ });
739
+
740
+ // L2 tick-level diffs (Pro+ tier)
741
+ const l2Diffs = await client.hyperliquid.l2Orderbook.diffs('BTC', {
742
+ start: Date.now() - 3600000,
743
+ end: Date.now(),
744
+ limit: 1000
745
+ });
746
+
747
+ // HIP-3 L2 orderbook
748
+ const hip3L2 = await client.hyperliquid.hip3.l2Orderbook.get('km:US500');
749
+ ```
750
+
751
+ ### Freshness
752
+
753
+ Check when each data type was last updated for a specific coin. Useful for verifying data recency before pulling it. Available across venue APIs.
754
+
755
+ ```typescript
756
+ // Hyperliquid
757
+ const freshness = await client.hyperliquid.freshness('BTC');
758
+ console.log(`Orderbook last updated: ${freshness.orderbook.lastUpdated}, lag: ${freshness.orderbook.lagMs}ms`);
759
+ console.log(`Trades last updated: ${freshness.trades.lastUpdated}, lag: ${freshness.trades.lagMs}ms`);
760
+ console.log(`Funding last updated: ${freshness.funding.lastUpdated}`);
761
+ console.log(`OI last updated: ${freshness.openInterest.lastUpdated}`);
762
+
763
+ // Lighter.xyz
764
+ const lighterFreshness = await client.lighter.freshness('BTC');
765
+
766
+ // HIP-3 (case-sensitive coins)
767
+ const hip3Freshness = await client.hyperliquid.hip3.freshness('km:US500');
768
+ ```
769
+
770
+ ### Summary
771
+
772
+ Get a combined market snapshot in a single call -- mark/oracle price, funding rate, open interest, 24h volume, and 24h liquidation volumes.
773
+
774
+ ```typescript
775
+ // Hyperliquid (includes volume + liquidation data)
776
+ const summary = await client.hyperliquid.summary('BTC');
777
+ console.log(`Mark price: ${summary.markPrice}`);
778
+ console.log(`Oracle price: ${summary.oraclePrice}`);
779
+ console.log(`Funding rate: ${summary.fundingRate}`);
780
+ console.log(`Open interest: ${summary.openInterest}`);
781
+ console.log(`24h volume: ${summary.volume24h}`);
782
+ console.log(`24h liquidation volume: $${summary.liquidationVolume24h}`);
783
+ console.log(` Long: $${summary.longLiquidationVolume24h}`);
784
+ console.log(` Short: $${summary.shortLiquidationVolume24h}`);
785
+
786
+ // Lighter.xyz (price, funding, OI no volume/liquidation data)
787
+ const lighterSummary = await client.lighter.summary('BTC');
788
+
789
+ // HIP-3 (includes mid_price case-sensitive coins)
790
+ const hip3Summary = await client.hyperliquid.hip3.summary('km:US500');
791
+ console.log(`Mid price: ${hip3Summary.midPrice}`);
792
+ ```
793
+
794
+ ### Price History
795
+
796
+ Get mark, oracle, and mid price history over time. Supports aggregation intervals. Data projected from open interest records.
797
+
798
+ ```typescript
799
+ // Hyperliquid — available from May 2023
800
+ const prices = await client.hyperliquid.priceHistory('BTC', {
801
+ start: Date.now() - 86400000,
802
+ end: Date.now(),
803
+ interval: '1h' // 5m, 15m, 30m, 1h, 4h, 1d
804
+ });
805
+
806
+ for (const snapshot of prices.data) {
807
+ console.log(`${snapshot.timestamp}: mark=${snapshot.markPrice}, oracle=${snapshot.oraclePrice}, mid=${snapshot.midPrice}`);
808
+ }
809
+
810
+ // Lighter.xyz
811
+ const lighterPrices = await client.lighter.priceHistory('BTC', {
812
+ start: Date.now() - 86400000,
813
+ end: Date.now(),
814
+ interval: '1h'
815
+ });
816
+
817
+ // HIP-3 (case-sensitive coins)
818
+ const hip3Prices = await client.hyperliquid.hip3.priceHistory('km:US500', {
819
+ start: Date.now() - 86400000,
820
+ end: Date.now(),
821
+ interval: '1d'
822
+ });
823
+
824
+ // Paginate for larger ranges
825
+ let result = await client.hyperliquid.priceHistory('BTC', {
826
+ start: Date.now() - 86400000 * 30,
827
+ end: Date.now(),
828
+ interval: '4h',
829
+ limit: 1000
830
+ });
831
+ while (result.nextCursor) {
832
+ result = await client.hyperliquid.priceHistory('BTC', {
833
+ start: Date.now() - 86400000 * 30,
834
+ end: Date.now(),
835
+ interval: '4h',
836
+ cursor: result.nextCursor,
837
+ limit: 1000
838
+ });
839
+ }
840
+ ```
841
+
842
+ ### Candles (OHLCV)
843
+
844
+ Get historical OHLCV candle data aggregated from trades.
845
+
846
+ ```typescript
847
+ // Get candle history (start is required)
848
+ const candles = await client.hyperliquid.candles.history('BTC', {
849
+ start: Date.now() - 86400000,
850
+ end: Date.now(),
851
+ interval: '1h', // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
852
+ limit: 100
853
+ });
854
+
855
+ // Iterate through candles
856
+ for (const candle of candles.data) {
857
+ console.log(`${candle.timestamp}: O=${candle.open} H=${candle.high} L=${candle.low} C=${candle.close} V=${candle.volume}`);
858
+ }
859
+
860
+ // Cursor-based pagination for large datasets
861
+ let result = await client.hyperliquid.candles.history('BTC', {
862
+ start: Date.now() - 86400000,
863
+ end: Date.now(),
864
+ interval: '1m',
865
+ limit: 1000
866
+ });
867
+ const allCandles = [...result.data];
868
+ while (result.nextCursor) {
869
+ result = await client.hyperliquid.candles.history('BTC', {
870
+ start: Date.now() - 86400000,
871
+ end: Date.now(),
872
+ interval: '1m',
873
+ cursor: result.nextCursor,
874
+ limit: 1000
875
+ });
876
+ allCandles.push(...result.data);
877
+ }
878
+
879
+ // Lighter.xyz candles
880
+ const lighterCandles = await client.lighter.candles.history('BTC', {
881
+ start: Date.now() - 86400000,
882
+ end: Date.now(),
883
+ interval: '15m'
884
+ });
885
+ ```
886
+
887
+ #### Available Intervals
888
+
889
+ | Interval | Description |
890
+ |----------|-------------|
891
+ | `1m` | 1 minute |
892
+ | `5m` | 5 minutes |
893
+ | `15m` | 15 minutes |
894
+ | `30m` | 30 minutes |
895
+ | `1h` | 1 hour (default) |
896
+ | `4h` | 4 hours |
897
+ | `1d` | 1 day |
898
+ | `1w` | 1 week |
899
+
900
+ ### Data Quality Monitoring
901
+
902
+ Monitor data coverage, incidents, latency, and SLA compliance across venue APIs.
903
+
904
+ ```typescript
905
+ // Get overall system health status
906
+ const status = await client.dataQuality.status();
907
+ console.log(`System status: ${status.status}`);
908
+ for (const [exchange, info] of Object.entries(status.exchanges)) {
909
+ console.log(` ${exchange}: ${info.status}`);
910
+ }
911
+
912
+ // Get data coverage summary for venue APIs
913
+ const coverage = await client.dataQuality.coverage();
914
+ for (const exchange of coverage.exchanges) {
915
+ console.log(`${exchange.exchange}:`);
916
+ for (const [dtype, info] of Object.entries(exchange.dataTypes)) {
917
+ console.log(` ${dtype}: ${info.totalRecords.toLocaleString()} records, ${info.completeness}% complete`);
918
+ }
919
+ }
920
+
921
+ // Get symbol-specific coverage with gap detection
922
+ const btc = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC');
923
+ const oi = btc.dataTypes.open_interest;
924
+ console.log(`BTC OI completeness: ${oi.completeness}%`);
925
+ console.log(`Historical coverage: ${oi.historicalCoverage}%`); // Hour-level granularity
926
+ console.log(`Gaps found: ${oi.gaps.length}`);
927
+ for (const gap of oi.gaps.slice(0, 5)) {
928
+ console.log(` ${gap.durationMinutes} min gap: ${gap.start} -> ${gap.end}`);
929
+ }
930
+
931
+ // Check empirical data cadence (when available)
932
+ const ob = btc.dataTypes.orderbook;
933
+ if (ob.cadence) {
934
+ console.log(`Orderbook cadence: ~${ob.cadence.medianIntervalSeconds}s median, p95=${ob.cadence.p95IntervalSeconds}s`);
935
+ }
936
+
937
+ // Time-bounded gap detection (last 7 days)
938
+ const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
939
+ const btc7d = await client.dataQuality.symbolCoverage('hyperliquid', 'BTC', {
940
+ from: weekAgo,
941
+ to: Date.now(),
942
+ });
943
+
944
+ // List incidents with filtering
945
+ const result = await client.dataQuality.listIncidents({ status: 'open' });
946
+ for (const incident of result.incidents) {
947
+ console.log(`[${incident.severity}] ${incident.title}`);
948
+ }
949
+
950
+ // Get latency metrics
951
+ const latency = await client.dataQuality.latency();
952
+ for (const [exchange, metrics] of Object.entries(latency.exchanges)) {
953
+ console.log(`${exchange}: OB lag ${metrics.dataFreshness.orderbookLagMs}ms`);
954
+ }
955
+
956
+ // Get SLA compliance metrics for a specific month
957
+ const sla = await client.dataQuality.sla({ year: 2026, month: 1 });
958
+ console.log(`Period: ${sla.period}`);
959
+ console.log(`Uptime: ${sla.actual.uptime}% (${sla.actual.uptimeStatus})`);
960
+ console.log(`API P99: ${sla.actual.apiLatencyP99Ms}ms (${sla.actual.latencyStatus})`);
961
+ ```
962
+
963
+ #### Data Quality Endpoints
964
+
965
+ | Method | Description |
966
+ |--------|-------------|
967
+ | `status()` | Overall system health and per-exchange status |
968
+ | `coverage()` | Data coverage summary for venue APIs |
969
+ | `exchangeCoverage(exchange)` | Coverage details for a specific exchange |
970
+ | `symbolCoverage(exchange, symbol, options?)` | Coverage with gap detection, cadence, and historical coverage |
971
+ | `listIncidents(params)` | List incidents with filtering and pagination |
972
+ | `getIncident(incidentId)` | Get specific incident details |
973
+ | `latency()` | Current latency metrics (WebSocket, REST, data freshness) |
974
+ | `sla(params)` | SLA compliance metrics for a specific month |
975
+
976
+ **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:
977
+
978
+ ```typescript
979
+ const client = new OxArchive({
980
+ apiKey: '0xa_your_api_key',
981
+ timeout: 60000 // 60 seconds for data quality endpoints
982
+ });
983
+ ```
984
+
985
+ ### Web3 Authentication
986
+
987
+ Get API keys programmatically using an Ethereum wallet — no browser or email required.
988
+
989
+ #### Free Tier (SIWE)
990
+
991
+ ```typescript
992
+ import { createWalletClient, http } from 'viem';
993
+ import { privateKeyToAccount } from 'viem/accounts';
994
+ import { mainnet } from 'viem/chains';
995
+
996
+ const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
997
+ const walletClient = createWalletClient({ account, chain: mainnet, transport: http() });
998
+
999
+ // 1. Get SIWE challenge
1000
+ const challenge = await client.web3.challenge(account.address);
1001
+
1002
+ // 2. Sign with personal_sign (EIP-191)
1003
+ const signature = await walletClient.signMessage({ message: challenge.message });
1004
+
1005
+ // 3. Submit → receive API key
1006
+ const result = await client.web3.signup(challenge.message, signature);
1007
+ console.log(result.apiKey); // "0xa_..."
1008
+ ```
1009
+
1010
+ #### Paid Tier (x402 USDC on Base)
1011
+
1012
+ ```typescript
1013
+ import { createWalletClient, http, encodePacked } from 'viem';
1014
+ import { privateKeyToAccount } from 'viem/accounts';
1015
+ import { base } from 'viem/chains';
1016
+ import crypto from 'crypto';
1017
+
1018
+ const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
1019
+ const walletClient = createWalletClient({ account, chain: base, transport: http() });
1020
+
1021
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
1022
+
1023
+ // 1. Get pricing
1024
+ const quote = await client.web3.subscribeQuote('build');
1025
+ // quote.amount = "49000000" ($49 USDC), quote.payTo = "0x..."
1026
+
1027
+ // 2. Build & sign EIP-3009 transferWithAuthorization
1028
+ const nonce = `0x${crypto.randomBytes(32).toString('hex')}` as `0x${string}`;
1029
+ const validAfter = 0n;
1030
+ const validBefore = BigInt(Math.floor(Date.now() / 1000) + 3600);
1031
+
1032
+ const signature = await walletClient.signTypedData({
1033
+ domain: {
1034
+ name: 'USD Coin',
1035
+ version: '2',
1036
+ chainId: 8453,
1037
+ verifyingContract: USDC_ADDRESS,
1038
+ },
1039
+ types: {
1040
+ TransferWithAuthorization: [
1041
+ { name: 'from', type: 'address' },
1042
+ { name: 'to', type: 'address' },
1043
+ { name: 'value', type: 'uint256' },
1044
+ { name: 'validAfter', type: 'uint256' },
1045
+ { name: 'validBefore', type: 'uint256' },
1046
+ { name: 'nonce', type: 'bytes32' },
1047
+ ],
1048
+ },
1049
+ primaryType: 'TransferWithAuthorization',
1050
+ message: {
1051
+ from: account.address,
1052
+ to: quote.payTo as `0x${string}`,
1053
+ value: BigInt(quote.amount),
1054
+ validAfter,
1055
+ validBefore,
1056
+ nonce,
1057
+ },
1058
+ });
1059
+
1060
+ // 3. Build x402 payment envelope and base64-encode
1061
+ const paymentPayload = btoa(JSON.stringify({
1062
+ x402Version: 2,
1063
+ payload: {
1064
+ signature,
1065
+ authorization: {
1066
+ from: account.address,
1067
+ to: quote.payTo,
1068
+ value: quote.amount,
1069
+ validAfter: '0',
1070
+ validBefore: validBefore.toString(),
1071
+ nonce,
1072
+ },
1073
+ },
1074
+ }));
1075
+
1076
+ // 4. Submit payment → receive API key + subscription
1077
+ const sub = await client.web3.subscribe('build', paymentPayload);
1078
+ console.log(sub.apiKey, sub.tier, sub.expiresAt);
1079
+ ```
1080
+
1081
+ #### Key Management
1082
+
1083
+ ```typescript
1084
+ // List and revoke keys (requires a fresh SIWE signature)
1085
+ const keys = await client.web3.listKeys(challenge.message, signature);
1086
+ await client.web3.revokeKey(challenge.message, signature, keys.keys[0].id);
1087
+ ```
1088
+
1089
+ ### Legacy API (Deprecated)
1090
+
1091
+ The following legacy methods are deprecated and will be removed in v2.0. They default to Hyperliquid data:
1092
+
1093
+ ```typescript
1094
+ // Deprecated - use client.hyperliquid.orderbook.get() instead
1095
+ const orderbook = await client.orderbook.get('BTC');
1096
+
1097
+ // Deprecated - use client.hyperliquid.trades.list() instead
1098
+ const trades = await client.trades.list('BTC', { start, end });
1099
+ ```
1100
+
1101
+ ## WebSocket Client
1102
+
1103
+ The WebSocket client supports two modes: real-time streaming and historical replay. For file-based historical exports, use the [Data Catalog](https://www.0xarchive.io/data).
1104
+
1105
+ ```typescript
1106
+ import { OxArchiveWs } from '@0xarchive/sdk';
1107
+
1108
+ const ws = new OxArchiveWs({ apiKey: '0xa_your_api_key' });
1109
+ ```
1110
+
1111
+ ### Real-time Streaming
1112
+
1113
+ Subscribe to live market data from Hyperliquid.
1114
+
1115
+ ```typescript
1116
+ ws.connect({
1117
+ onOpen: () => console.log('Connected'),
1118
+ onClose: (code, reason) => console.log(`Disconnected: ${code}`),
1119
+ onError: (error) => console.error('Error:', error),
1120
+ });
1121
+
1122
+ // Subscribe to channels
1123
+ ws.subscribeOrderbook('BTC');
1124
+ ws.subscribeTrades('ETH');
1125
+ ws.subscribeTicker('SOL');
1126
+ ws.subscribeAllTickers();
1127
+
1128
+ // Handle real-time data with typed callbacks
1129
+ ws.onOrderbook((coin, data) => {
1130
+ console.log(`${coin} mid price: ${data.midPrice}`);
1131
+ });
1132
+
1133
+ ws.onTrades((coin, trades) => {
1134
+ console.log(`${coin} new trades: ${trades.length}`);
1135
+ });
1136
+
1137
+ // Unsubscribe when done
1138
+ ws.unsubscribeOrderbook('BTC');
1139
+
1140
+ // Disconnect
1141
+ ws.disconnect();
1142
+ ```
1143
+
1144
+ ### Historical Replay
1145
+
1146
+ Replay historical data with original timing preserved. Perfect for backtesting.
1147
+
1148
+ > **Important:** Replay data is delivered via `onHistoricalData()`, NOT `onTrades()` or `onOrderbook()`.
1149
+ > The real-time callbacks only receive live market data from subscriptions.
1150
+
1151
+ ```typescript
1152
+ const ws = new OxArchiveWs({ apiKey: 'ox_...' });
1153
+ ws.connect();
1154
+
1155
+ // Handle replay data - this is where historical records arrive
1156
+ ws.onHistoricalData((coin, timestamp, data) => {
1157
+ console.log(`${new Date(timestamp).toISOString()}: ${data.midPrice}`);
1158
+ });
1159
+
1160
+ // Replay lifecycle events
1161
+ ws.onReplayStart((channel, coin, start, end, speed) => {
1162
+ console.log(`Starting replay: ${channel}/${coin} at ${speed}x`);
1163
+ });
1164
+
1165
+ ws.onReplayComplete((channel, coin, recordsSent) => {
1166
+ console.log(`Replay complete: ${recordsSent} records`);
1167
+ });
1168
+
1169
+ // Start replay at 10x speed
1170
+ ws.replay('orderbook', 'BTC', {
1171
+ start: Date.now() - 86400000, // 24 hours ago
1172
+ end: Date.now(), // Optional, defaults to now
1173
+ speed: 10 // Optional, defaults to 1x
1174
+ });
1175
+
1176
+ // Lighter.xyz replay with granularity (tier restrictions apply)
1177
+ ws.replay('orderbook', 'BTC', {
1178
+ start: Date.now() - 86400000,
1179
+ speed: 10,
1180
+ granularity: '10s' // Options: 'checkpoint', '30s', '10s', '1s', 'tick'
1181
+ });
1182
+
1183
+ // Handle tick-level data (granularity='tick', Enterprise tier)
1184
+ ws.onHistoricalTickData((coin, checkpoint, deltas) => {
1185
+ console.log(`Checkpoint: ${checkpoint.bids.length} bids`);
1186
+ console.log(`Deltas: ${deltas.length} updates`);
1187
+ // Apply deltas to checkpoint to reconstruct orderbook at any point
1188
+ });
1189
+
1190
+ // Control playback
1191
+ ws.replayPause();
1192
+ ws.replayResume();
1193
+ ws.replaySeek(1704067200000); // Jump to timestamp
1194
+ ws.replayStop();
1195
+ ```
1196
+
1197
+ ### Gap Detection
1198
+
1199
+ During historical replay, the server automatically detects gaps in the data and notifies the client. This helps identify periods where data may be missing.
1200
+
1201
+ ```typescript
1202
+ // Handle gap notifications during replay
1203
+ ws.onGap((channel, coin, gapStart, gapEnd, durationMinutes) => {
1204
+ console.log(`Gap detected in ${channel}/${coin}:`);
1205
+ console.log(` From: ${new Date(gapStart).toISOString()}`);
1206
+ console.log(` To: ${new Date(gapEnd).toISOString()}`);
1207
+ console.log(` Duration: ${durationMinutes} minutes`);
1208
+ });
1209
+
1210
+ // Start replay - gaps will be reported via onGap callback
1211
+ ws.replay('orderbook', 'BTC', {
1212
+ start: Date.now() - 86400000,
1213
+ end: Date.now(),
1214
+ speed: 10
1215
+ });
1216
+ ```
1217
+
1218
+ Gap thresholds vary by channel:
1219
+ - **orderbook**, **candles**, **liquidations**: 2 minutes
1220
+ - **trades**: 60 minutes (trades can naturally have longer gaps during low activity periods)
1221
+
1222
+ ### WebSocket Configuration
1223
+
1224
+ ```typescript
1225
+ const ws = new OxArchiveWs({
1226
+ apiKey: '0xa_your_api_key', // Required
1227
+ wsUrl: 'wss://api.0xarchive.io/ws', // Optional
1228
+ autoReconnect: true, // Auto-reconnect on disconnect (default: true)
1229
+ reconnectDelay: 1000, // Initial reconnect delay in ms (default: 1000)
1230
+ maxReconnectAttempts: 10, // Max reconnect attempts (default: 10)
1231
+ pingInterval: 30000, // Keep-alive ping interval in ms (default: 30000)
1232
+ });
1233
+ ```
1234
+
1235
+ ### Available Channels
1236
+
1237
+ #### Hyperliquid Channels
1238
+
1239
+ | Channel | Description | Requires Coin | Mode |
1240
+ |---------|-------------|---------------|-------------------|
1241
+ | `orderbook` | L2 order book updates | Yes | Realtime + replay |
1242
+ | `trades` | Trade/fill updates | Yes | Realtime + replay |
1243
+ | `candles` | OHLCV candle data | Yes | Replay only |
1244
+ | `liquidations` | Liquidation events (May 2025+) | Yes | Realtime + replay (live as of 1.6.0) |
1245
+ | `open_interest` | Open interest snapshots | Yes | Replay/stream only |
1246
+ | `funding` | Funding rate snapshots | Yes | Replay/stream only |
1247
+ | `ticker` | Price and 24h volume | Yes | Real-time only |
1248
+ | `all_tickers` | All market tickers | No | Real-time only |
1249
+ | `l4_diffs` | L4 orderbook diffs with user attribution (Pro+) | Yes | Real-time only |
1250
+ | `l4_orders` | Order lifecycle events with user attribution (Pro+) | Yes | Real-time only |
1251
+
1252
+ 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[]`.
1253
+
1254
+ #### HIP-3 Builder Perps Channels
1255
+
1256
+ | Channel | Description | Requires Coin | Mode |
1257
+ |---------|-------------|---------------|-------------------|
1258
+ | `hip3_orderbook` | HIP-3 L2 order book snapshots | Yes | Realtime + replay |
1259
+ | `hip3_trades` | HIP-3 trade/fill updates | Yes | Realtime + replay |
1260
+ | `hip3_candles` | HIP-3 OHLCV candle data | Yes | Realtime + replay |
1261
+ | `hip3_open_interest` | HIP-3 open interest snapshots | Yes | Replay/stream only |
1262
+ | `hip3_funding` | HIP-3 funding rate snapshots | Yes | Replay/stream only |
1263
+ | `hip3_liquidations` | HIP-3 liquidation events (Feb 2026+) | Yes | Realtime + replay (live as of 1.6.0) |
1264
+ | `hip3_l4_diffs` | HIP-3 L4 orderbook diffs (Pro+) | Yes | Real-time only |
1265
+ | `hip3_l4_orders` | HIP-3 order lifecycle events (Pro+) | Yes | Real-time only |
1266
+
1267
+ > **Note:** HIP-3 coins are case-sensitive (e.g., `km:US500`, `xyz:XYZ100`). Do not uppercase them.
1268
+
1269
+ #### HIP-4 Outcome-Market Channels
1270
+
1271
+ | Channel | Description | Requires Coin | Mode |
1272
+ |---------|-------------|---------------|-------------------|
1273
+ | `hip4_orderbook` | HIP-4 L2 order book snapshots (Pro+) | Yes | Realtime + replay |
1274
+ | `hip4_trades` | HIP-4 trade/fill updates | Yes | Realtime + replay |
1275
+ | `hip4_open_interest` | HIP-4 open interest (per side) | Yes | Realtime + replay |
1276
+ | `hip4_l4_diffs` | HIP-4 L4 orderbook diffs (Pro+) | Yes | Real-time only |
1277
+ | `hip4_l4_orders` | HIP-4 order lifecycle events (Pro+) | Yes | Real-time only |
1278
+
1279
+ HIP-4 has no funding, no liquidations, no candles by design (markets settle to 0/1 at expiry). HIP-4 `mark_price` and `midPrice` are implied probabilities in `[0, 1]`, not USD prices.
1280
+
1281
+ 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.
1282
+
1283
+ ```typescript
1284
+ ws.onOrderbook((coin, ob) => {
1285
+ // For HIP-4, ob.midPrice is a probability ∈ [0, 1]
1286
+ console.log(`${coin} implied probability: ${ob.midPrice}`);
1287
+ });
1288
+ ws.subscribeHip4('hip4_orderbook', '0');
1289
+ ws.subscribeHip4('hip4_trades', '#1');
1290
+
1291
+ // Settlement signal — terminal for the coin
1292
+ ws.onOutcomeSettled((coin, outcomeId, side, value, at) => {
1293
+ console.log(`${coin} (outcome ${outcomeId} side ${side}) settled: ${value} at ${at}`);
1294
+ });
1295
+ ```
1296
+
1297
+ #### Hyperliquid Spot Channels
1298
+
1299
+ | Channel | Description | Requires Coin | Mode |
1300
+ |---------|-------------|---------------|-------------------|
1301
+ | `spot_orderbook` | Spot L2 order book snapshots (Build+) | Yes | Real-time only |
1302
+ | `spot_trades` | Spot trade/fill updates (Build+) | Yes | Real-time only |
1303
+ | `spot_l4_diffs` | Spot L4 orderbook diffs (Pro+) | Yes | Real-time only |
1304
+ | `spot_l4_orders` | Spot order lifecycle events (Pro+) | Yes | Real-time only |
1305
+ | `spot_twap` | Spot TWAP statuses (Build+) | Yes | Real-time only |
1306
+
1307
+ Spot symbols are dashed canonical (`HYPE-USDC`, `PURR-USDC`). The server resolves the dashed form to wire format internally. Spot has no funding, no open interest, no liquidations, and no candles by design.
1308
+
1309
+ ```typescript
1310
+ ws.onOrderbook((coin, ob) => {
1311
+ console.log(`${coin} mid: ${ob.midPrice}`);
1312
+ });
1313
+ ws.onTrades((coin, trades) => {
1314
+ console.log(`${coin} got ${trades.length} trades`);
1315
+ });
1316
+
1317
+ await ws.connect();
1318
+ ws.subscribeSpot('orderbook', 'HYPE-USDC');
1319
+ ws.subscribeSpot('trades', 'HYPE-USDC');
1320
+
1321
+ // Pro+: L4 channels
1322
+ ws.subscribeSpot('l4_diffs', 'HYPE-USDC');
1323
+ ws.subscribeSpot('l4_orders', 'HYPE-USDC');
1324
+
1325
+ // TWAP statuses
1326
+ ws.subscribeSpot('twap', 'HYPE-USDC');
1327
+ ```
1328
+
1329
+ #### Live Liquidations
1330
+
1331
+ ```typescript
1332
+ import { OxArchiveWs } from '@0xarchive/sdk';
1333
+
1334
+ const ws = new OxArchiveWs({ apiKey: 'ox_...' });
1335
+ ws.onLiquidations((channel, coin, fills) => {
1336
+ for (const f of fills) {
1337
+ console.log(`${channel} ${coin}: ${f.side} ${f.size}@${f.price} liq`);
1338
+ }
1339
+ });
1340
+ await ws.connect();
1341
+
1342
+ ws.subscribeLiquidations('BTC');
1343
+ ws.subscribeHip3Liquidations('hyna:BTC');
1344
+ ```
1345
+
1346
+ #### Lighter.xyz Channels
1347
+
1348
+ | Channel | Description | Requires Coin | Historical Support |
1349
+ |---------|-------------|---------------|-------------------|
1350
+ | `lighter_orderbook` | Lighter L2 order book (reconstructed) | Yes | Yes |
1351
+ | `lighter_trades` | Lighter trade/fill updates | Yes | Yes |
1352
+ | `lighter_candles` | Lighter OHLCV candle data | Yes | Yes |
1353
+ | `lighter_open_interest` | Lighter open interest snapshots | Yes | Replay/stream only |
1354
+ | `lighter_funding` | Lighter funding rate snapshots | Yes | Replay/stream only |
1355
+ | `lighter_l3_orderbook` | Lighter L3 order-level orderbook (Pro+) | Yes | Yes |
1356
+
1357
+ #### Candle Replay/Stream
1358
+
1359
+ ```typescript
1360
+ // Replay candles at 10x speed
1361
+ ws.replay('candles', 'BTC', {
1362
+ start: Date.now() - 86400000,
1363
+ end: Date.now(),
1364
+ speed: 10,
1365
+ interval: '15m' // 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w
1366
+ });
1367
+
1368
+ // Lighter.xyz candles
1369
+ ws.replay('lighter_candles', 'BTC', {
1370
+ start: Date.now() - 86400000,
1371
+ speed: 10,
1372
+ interval: '5m'
1373
+ });
1374
+ ```
1375
+
1376
+ #### HIP-3 Replay
1377
+
1378
+ ```typescript
1379
+ // Replay HIP-3 orderbook at 50x speed
1380
+ ws.replay('hip3_orderbook', 'km:US500', {
1381
+ start: Date.now() - 3600000,
1382
+ end: Date.now(),
1383
+ speed: 50,
1384
+ });
1385
+
1386
+ // HIP-3 candles
1387
+ ws.replay('hip3_candles', 'km:US500', {
1388
+ start: Date.now() - 86400000,
1389
+ end: Date.now(),
1390
+ speed: 100,
1391
+ interval: '1h'
1392
+ });
1393
+ ```
1394
+
1395
+ ### Multi-Channel Replay
1396
+
1397
+ 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.
1398
+
1399
+ ```typescript
1400
+ const ws = new OxArchiveWs({ apiKey: 'ox_...' });
1401
+ await ws.connect();
1402
+
1403
+ // Handle initial snapshots (sent before timeline data)
1404
+ ws.onReplaySnapshot((channel, coin, timestamp, data) => {
1405
+ console.log(`Initial ${channel} state at ${new Date(timestamp).toISOString()}`);
1406
+ if (channel === 'orderbook') {
1407
+ currentOrderbook = data;
1408
+ } else if (channel === 'funding') {
1409
+ currentFundingRate = data;
1410
+ } else if (channel === 'open_interest') {
1411
+ currentOI = data;
1412
+ }
1413
+ });
1414
+
1415
+ // Handle interleaved historical data
1416
+ ws.onHistoricalData((coin, timestamp, data) => {
1417
+ // The `channel` field on the raw message indicates which channel
1418
+ // this data point belongs to
1419
+ console.log(`${new Date(timestamp).toISOString()}: data received`);
1420
+ });
1421
+
1422
+ ws.onReplayComplete((channel, coin, count) => {
1423
+ console.log(`Replay complete: ${count} records`);
1424
+ });
1425
+
1426
+ // Start multi-channel replay
1427
+ ws.multiReplay(['orderbook', 'trades', 'funding'], 'BTC', {
1428
+ start: Date.now() - 86400000,
1429
+ end: Date.now(),
1430
+ speed: 10
1431
+ });
1432
+
1433
+ // Playback controls work the same as single-channel
1434
+ ws.replayPause();
1435
+ ws.replayResume();
1436
+ ws.replaySeek(1704067200000);
1437
+ ws.replayStop();
1438
+ ```
1439
+
1440
+ **Channels available for multi-channel replay:** All historical channels can be combined in a single multi-channel replay. This includes `orderbook`, `trades`, `candles`, `liquidations`, `open_interest`, `funding`, and their `lighter_*` and `hip3_*` variants.
1441
+
1442
+ ### WebSocket Connection States
1443
+
1444
+ ```typescript
1445
+ ws.getState(); // 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
1446
+ ws.isConnected(); // boolean
1447
+ ```
1448
+
1449
+ ## Timestamp Formats
1450
+
1451
+ The SDK accepts timestamps as Unix milliseconds or Date objects:
1452
+
1453
+ ```typescript
1454
+ // Unix milliseconds (recommended)
1455
+ client.hyperliquid.orderbook.history('BTC', {
1456
+ start: Date.now() - 86400000,
1457
+ end: Date.now()
1458
+ });
1459
+
1460
+ // Date objects (converted automatically)
1461
+ client.hyperliquid.orderbook.history('BTC', {
1462
+ start: new Date('2024-01-01'),
1463
+ end: new Date('2024-01-02')
1464
+ });
1465
+
1466
+ // WebSocket replay also accepts both
1467
+ ws.replay('orderbook', 'BTC', {
1468
+ start: Date.now() - 3600000,
1469
+ end: Date.now(),
1470
+ speed: 10
1471
+ });
1472
+ ```
1473
+
1474
+ ## Error Handling
1475
+
1476
+ ```typescript
1477
+ import { OxArchive, OxArchiveError } from '@0xarchive/sdk';
1478
+
1479
+ try {
1480
+ const orderbook = await client.orderbook.get('INVALID');
1481
+ } catch (error) {
1482
+ if (error instanceof OxArchiveError) {
1483
+ console.error(`API Error: ${error.message}`);
1484
+ console.error(`Status Code: ${error.code}`);
1485
+ console.error(`Request ID: ${error.requestId}`);
1486
+ }
1487
+ }
1488
+ ```
1489
+
1490
+ ## TypeScript Support
1491
+
1492
+ Full TypeScript support with exported types:
1493
+
1494
+ ```typescript
1495
+ import type {
1496
+ OrderBook,
1497
+ PriceLevel,
1498
+ Trade,
1499
+ Candle,
1500
+ Instrument,
1501
+ LighterInstrument,
1502
+ Hip3Instrument,
1503
+ LighterGranularity,
1504
+ FundingRate,
1505
+ OpenInterest,
1506
+ Liquidation,
1507
+ LiquidationVolume,
1508
+ CoinFreshness,
1509
+ CoinSummary,
1510
+ PriceSnapshot,
1511
+ CursorResponse,
1512
+ WsOptions,
1513
+ WsChannel,
1514
+ WsConnectionState,
1515
+ WsReplaySnapshot,
1516
+ // Orderbook reconstruction (Enterprise)
1517
+ OrderbookDelta,
1518
+ TickData,
1519
+ ReconstructedOrderBook,
1520
+ ReconstructOptions,
1521
+ TickHistoryParams,
1522
+ } from '@0xarchive/sdk';
1523
+
1524
+ // Import reconstructor class
1525
+ import { OrderBookReconstructor } from '@0xarchive/sdk';
1526
+ ```
1527
+
1528
+ ## Runtime Validation
1529
+
1530
+ Enable Zod schema validation for API responses:
1531
+
1532
+ ```typescript
1533
+ const client = new OxArchive({
1534
+ apiKey: '0xa_your_api_key',
1535
+ validate: true // Enable runtime validation
1536
+ });
1537
+ ```
1538
+
1539
+ When enabled, responses are validated against Zod schemas and throw `OxArchiveError` with status 422 if validation fails.
1540
+
1541
+ ## Data Catalog
1542
+
1543
+ For large-scale data exports (full order books, complete trade history, etc.), use the [Data Catalog](https://www.0xarchive.io/data). It lets you choose markets, datasets, and date ranges, see a live quote, and export zstd-compressed Parquet.
1544
+
1545
+ ## Requirements
1546
+
1547
+ - Node.js 18+ or modern browsers with `fetch` and `WebSocket` support
1548
+
1549
+ ## License
1550
+
1551
+ MIT