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