Official TypeScript SDK for the Luzia cryptocurrency pricing API.
bun add @luziadev/sdk
# or
npm install @luziadev/sdkimport { Luzia } from '@luziadev/sdk'
const luzia = new Luzia({
apiKey: 'lz_your_api_key',
})
// List all supported exchanges
const exchanges = await luzia.exchanges.list()
console.log(exchanges)
// [{ id: 'binance', name: 'Binance', status: 'operational', ... }]
// Get a single ticker
const ticker = await luzia.tickers.get('binance', 'BTC/USDT')
console.log(`BTC/USDT: $${ticker.last}`)
// Get all tickers for an exchange
const { tickers, total } = await luzia.tickers.list('binance', { limit: 50 })
console.log(`Found ${total} tickers`)
// Get markets with filters
const { markets } = await luzia.markets.list('binance', { quote: 'USDT' })
// Get historical OHLCV candles
const { candles } = await luzia.history.get('binance', 'BTC/USDT', {
interval: '1h',
limit: 24,
})
console.log(`Last close: $${candles[candles.length - 1].close}`)const luzia = new Luzia({
// Required: Your API key
apiKey: 'lz_xxxxx',
// Optional: Custom base URL (default: https://api.luzia.dev)
baseUrl: 'http://localhost:3000',
// Optional: Request timeout in ms (default: 30000)
timeout: 10000,
// Optional: Retry configuration
retry: {
maxRetries: 3, // Number of retry attempts (default: 3)
initialDelayMs: 1000, // Initial delay before first retry (default: 1000)
maxDelayMs: 30000, // Maximum delay between retries (default: 30000)
backoffMultiplier: 2, // Multiplier for exponential backoff (default: 2)
jitter: true, // Add random jitter to delays (default: true)
},
})// List all supported exchanges
const exchanges = await luzia.exchanges.list()// Get a single ticker
const ticker = await luzia.tickers.get('binance', 'BTC/USDT')
// List all tickers for an exchange
const { tickers, total, limit, offset } = await luzia.tickers.list('binance', {
limit: 50,
offset: 0,
})
// Get specific tickers across exchanges
const { tickers } = await luzia.tickers.listFiltered({
exchange: 'binance', // Optional: filter by exchange
symbols: ['BTC/USDT', 'ETH/USDT'], // Optional: filter by symbols
limit: 100,
offset: 0,
})// List markets for an exchange
const { markets, total } = await luzia.markets.list('binance', {
base: 'BTC', // Optional: filter by base currency
quote: 'USDT', // Optional: filter by quote currency
active: true, // Optional: filter by active status
limit: 100,
offset: 0,
})// Get 24h of hourly candles (default)
const { candles, count } = await luzia.history.get('binance', 'BTC/USDT')
// Specify interval and limit
const { candles } = await luzia.history.get('binance', 'ETH/USDT', {
interval: '15m', // '1m' | '5m' | '15m' | '1h' | '1d'
limit: 96, // Number of candles (max: 500)
})
// Specify a time range
const { candles } = await luzia.history.get('binance', 'BTC/USDT', {
interval: '1d',
start: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days ago (Unix ms)
end: Date.now(),
})
// Each candle contains:
for (const candle of candles) {
console.log({
timestamp: candle.timestamp, // Candle open time (RFC 3339 string)
open: candle.open,
high: candle.high,
low: candle.low,
close: candle.close,
volume: candle.volume, // Base currency volume
quoteVolume: candle.quoteVolume,
trades: candle.trades, // Number of trades in interval
})
}Lookback limits by tier:
| Tier | Max Lookback |
|---|---|
| Free | 30 days |
| Pro | 90 days |
Stream live ticker updates over WebSocket. Requires a Pro plan or higher.
import { Luzia } from '@luziadev/sdk'
const luzia = new Luzia({ apiKey: 'lz_your_api_key' })
const ws = luzia.createWebSocket()
ws.on('connected', (info) => {
console.log(`Connected (${info.tier}), max subscriptions: ${info.limits.maxSubscriptions}`)
ws.subscribe(['ticker:binance:BTC/USDT', 'ticker:coinbase:ETH/USDT'])
})
ws.on('ticker', (data) => {
console.log(`${data.exchange} ${data.symbol}: $${data.data.last}`)
})
ws.on('error', (err) => {
console.error(`WebSocket error [${err.code}]: ${err.message}`)
})
ws.connect()ticker:{exchange}:{symbol}β specific pair (e.g.,ticker:binance:BTC/USDT)ticker:{exchange}β all tickers from an exchange (e.g.,ticker:binance)
// Subscribe to channels (can be called before or after connect)
ws.subscribe(['ticker:binance:BTC/USDT', 'ticker:kraken:ETH/USDT'])
// Unsubscribe from channels
ws.unsubscribe(['ticker:binance:BTC/USDT'])
// Check active subscriptions
console.log(ws.subscriptions) // ReadonlySet<string>const ws = luzia.createWebSocket({
autoReconnect: true, // Auto-reconnect on disconnection (default: true)
maxReconnectAttempts: 10, // Max attempts, 0 = infinite (default: 10)
reconnectDelayMs: 1000, // Initial reconnect delay (default: 1000ms)
maxReconnectDelayMs: 30000, // Max reconnect delay (default: 30000ms)
heartbeatIntervalMs: 30000, // Heartbeat interval, 0 = disabled (default: 30000ms)
})| Event | Payload | Description |
|---|---|---|
connected |
{ tier, limits } |
Connection established, server ready |
ticker |
{ exchange, symbol, data, timestamp } |
Price update received |
subscribed |
{ channel } |
Channel subscription confirmed |
unsubscribed |
{ channel } |
Channel unsubscription confirmed |
error |
{ code, message } |
Error from server or connection |
disconnected |
{ code, reason } |
Connection closed |
reconnecting |
{ attempt, delayMs } |
Reconnect attempt starting |
console.log(ws.state) // 'disconnected' | 'connecting' | 'connected' | 'reconnecting'// Gracefully close and disable auto-reconnect
ws.disconnect()| Tier | Connections | Subscriptions / Connection |
|---|---|---|
| Free | Not available | β |
| Pro | 5 | 50 |
| Enterprise | 25 | 500 |
Check your balance, view transaction history, discover pricing, and top up your account.
const balance = await luzia.billing.getBalance()
console.log(`Balance: $${balance.balance_usd}`)
console.log(`Lifetime spent: $${balance.lifetime_spent_usd}`)// Public endpoint (no auth required)
const pricing = await luzia.billing.getPricing()
for (const entry of pricing.rest) {
console.log(`${entry.endpoint}: $${entry.cost_usd}/${entry.unit}`)
}const { transactions, total } = await luzia.billing.getTransactions({
page: 1,
limit: 20,
type: 'debit', // Optional: 'debit' | 'credit' | 'refund' | 'bonus'
})
for (const tx of transactions) {
console.log(`${tx.type}: ${tx.amount_usd} - ${tx.description}`)
}// Initiate a top-up ($5, $10, $25, $50, or $100)
const { checkout_url } = await luzia.billing.topUp(1000) // $10.00
// Redirect user to checkout_url to complete paymentEvery authenticated API response includes balance information in headers. Access it via balanceInfo:
await luzia.tickers.get('binance', 'BTC/USDT')
const info = luzia.balanceInfo
if (info) {
console.log(`Remaining: $${info.balance}`)
console.log(`Last request cost: $${info.cost}`)
}import { InsufficientBalanceError } from '@luziadev/sdk'
try {
await luzia.tickers.get('binance', 'BTC/USDT')
} catch (error) {
if (error instanceof InsufficientBalanceError) {
console.log(`Balance: $${error.balance}`)
console.log(`Top up at: ${error.topUpUrl}`)
}
}The SDK uses a single LuziaError class with a code property to distinguish error types:
import { Luzia, LuziaError } from '@luziadev/sdk'
try {
const ticker = await luzia.tickers.get('invalid', 'BTC/USDT')
} catch (error) {
if (error instanceof LuziaError) {
switch (error.code) {
case 'auth':
console.log('Invalid API key')
break
case 'insufficient_balance':
console.log(`Balance too low: $${error.balanceUsd}. Top up: ${error.topUpUrl}`)
break
case 'rate_limit':
console.log(`Rate limited. Retry after ${error.retryAfter} seconds`)
console.log(`Limit info:`, error.rateLimitInfo)
break
case 'not_found':
console.log('Resource not found')
break
case 'validation':
console.log('Invalid request parameters:', error.details)
break
case 'server':
console.log('Server error (exchange may be temporarily unavailable)')
break
case 'network':
console.log('Network error:', error.message)
break
case 'timeout':
console.log(`Request timed out after ${error.timeoutMs}ms`)
break
}
}
}import { isLuziaError, isRetryableError } from '@luziadev/sdk'
if (isLuziaError(error)) {
console.log(`Luzia error: ${error.message}`)
}
if (isRetryableError(error)) {
console.log('This error can be retried')
}Access rate limit information from the most recent request:
const ticker = await luzia.tickers.get('binance', 'BTC/USDT')
const info = luzia.rateLimitInfo
if (info) {
console.log(`Requests remaining: ${info.remaining}/${info.limit}`)
console.log(`Resets at: ${new Date(info.reset * 1000)}`)
// Free tier also has daily limits
if (info.dailyLimit) {
console.log(`Daily remaining: ${info.dailyRemaining}/${info.dailyLimit}`)
}
}The SDK automatically retries requests on:
- Rate limit errors (429) - respects
Retry-Afterheader - Timeout errors (408)
- Server errors (500, 502, 503, 504)
- Network errors
Non-retryable errors (400, 401, 402, 403, 404) are thrown immediately.
import { withRetry } from '@luziadev/sdk'
// Use the retry utility directly
const result = await withRetry(
() => luzia.tickers.get('binance', 'BTC/USDT'),
{ maxRetries: 5 },
(context) => {
console.log(`Retry attempt ${context.attempt + 1}/${context.maxRetries}`)
}
)All types are exported for use in your application:
import type {
// REST API
Exchange,
ExchangeStatus,
Market,
Ticker,
OHLCVCandle,
OHLCVResponse,
CandleInterval,
GetHistoryOptions,
RateLimitInfo,
LuziaOptions,
RetryOptions,
// Billing
BalanceInfo,
BalanceResponse,
BalanceTransaction,
PricingResponse,
TopUpResponse,
TopUpAmount,
TransactionsResponse,
TransactionType,
ListTransactionsOptions,
// WebSocket
WebSocketOptions,
WSTickerData,
WSConnectedData,
WSErrorData,
WSEventMap,
WSConnectionState,
} from '@luziadev/sdk'# Install dependencies
bun install
# Generate types from OpenAPI spec
bun run generate
# Type check
bun run typecheck
# Run tests
bun test- npm package
- GitHub repository
- REST API documentation
- WebSocket API documentation
- Changelog
- Contributing guide
MIT