TypeScript-first, zero-dependency SDK for the Delta Exchange India crypto derivatives API.
- Full REST + WebSocket coverage of Delta India's v2 API
- Complete TypeScript types for every request and response
- Zero runtime dependencies — uses native
fetchandWebSocket - Dual ESM + CommonJS build with
.d.tsfor both - Built-in HMAC SHA256 request signing
- Sliding-window rate limiter with auto-queue or throw modes
- Auto-reconnect WebSocket with exponential backoff and ping/pong
- Node.js 22 or later (uses native
fetchandWebSocket)
npm install delta-india-sdkGenerate an API key + secret from your Delta India account (or testnet). For trading you must enable the "Trade" permission. Store the secret in an environment variable, never in source code.
export DELTA_API_KEY=...
export DELTA_API_SECRET=...import { DeltaClient } from 'delta-india-sdk';
const client = new DeltaClient({
apiKey: process.env.DELTA_API_KEY,
apiSecret: process.env.DELTA_API_SECRET,
testnet: true,
});
// Public market data — no API key needed
const ticker = await client.products.ticker('BTCUSD');
console.log(`BTCUSD mark: ${ticker.mark_price}`);
// Authenticated call
const balances = await client.wallet.balances();
client.destroy(); // releases the rate-limiter timerconst order = await client.orders.create({
product_symbol: 'BTCUSD',
size: '1', // contracts, not USD
side: 'buy',
order_type: 'limit_order',
limit_price: '60000',
time_in_force: 'gtc',
});await client.orders.cancel(order.id, order.product_id);Delta India's cancel is
DELETE /v2/orderswith{id, product_id}in the body —cancel(id, productId)handles that for you.
The unfiltered /v2/tickers returns 10 000+ rows for options. Always filter:
const callsAndPuts = await client.products.tickers({
contract_types: 'call_options,put_options',
underlying_asset_symbols: 'BTC',
expiry_date: '27-12-2026', // DD-MM-YYYY
});
for (const t of callsAndPuts) {
console.log(t.symbol, t.strike_price, t.greeks?.delta);
}// Single position by product
const btcPerpPos = await client.positions.list({ product_id: 27 });
// All positions on an underlying
const ethPositions = await client.positions.list({
underlying_asset_symbol: 'ETH',
});
list()returnsPosition[]even when Delta responds with a single object (filtered byproduct_id).
await client.orders.create({
product_id: 27,
size: '1',
side: 'buy',
order_type: 'market_order',
bracket_stop_loss_price: '58000',
bracket_stop_trigger_method: 'mark_price', // or 'last_traded_price', 'spot_price'
bracket_take_profit_price: '65000',
});await client.orders.batchCreate({
product_id: 27, // required at the top level
orders: [
{ size: '1', side: 'buy', order_type: 'limit_order', limit_price: '59000' },
{ size: '1', side: 'sell', order_type: 'limit_order', limit_price: '61000' },
],
});
await client.orders.batchDelete({
product_id: 27,
orders: [{ id: 1234 }, { id: 1235 }],
});client.ws.on('connected', () => {
client.ws.subscribe('v2/ticker', ['BTCUSD']);
client.ws.subscribe('recent_trade', ['BTCUSD']);
});
client.ws.on('v2/ticker', (data) => console.log(data.symbol, data.close));
client.ws.on('recent_trade', (trade) => console.log('trade', trade));
client.ws.connect();Delta sends recent-trade messages typed
all_trades/all_trades_snapshot; the SDK re-emits them under the canonicalrecent_tradeevent so your handler always fires.
const client = new DeltaClient({
apiKey: process.env.DELTA_API_KEY,
apiSecret: process.env.DELTA_API_SECRET,
});
client.ws.on('connected', () => {
client.ws.subscribe('orders');
client.ws.subscribe('positions');
});
client.ws.on('authenticated', () => console.log('private channels live'));
client.ws.on('orders', (o) => console.log('order update', o.id, o.state));
client.ws.connect();Delta returns most numeric fields (size, entry_price, mark_price, greeks.delta, quotes.best_bid, …) as strings to preserve precision. Convert them at the edge using the bundled num() / int() helpers instead of scattering parseFloat(x) || 0 through your code:
import { DeltaClient, num, int } from 'delta-india-sdk';
const [pos] = await client.positions.list({ product_id: 27 });
const size = num(pos.size); // "1" -> 1
const entry = num(pos.entry_price ?? pos.avg_entry_price); // string-or-null -> number
const mark = num(pos.mark_price, num(ticker.mark_price)); // chained fallback
const productId = int(req.query.productId, 27); // URL/DB string -> intBoth helpers accept string | number | null | undefined and return a finite number, falling back to 0 (or your supplied default) for null, empty, NaN, and Infinity. int() additionally truncates the result.
const client = new DeltaClient({
apiKey: 'your-api-key', // required for authenticated endpoints + private WS
apiSecret: 'your-api-secret', // required for authenticated endpoints + private WS
testnet: true, // default false
baseUrl: 'https://...', // optional override (takes precedence over testnet)
wsUrl: 'wss://...', // optional WebSocket override
timeout: 10000, // ms, default 10000
rateLimitMode: 'auto', // 'auto' queues, 'manual' throws RateLimitError
maxQueueSize: 100, // queued requests cap in auto mode
});| Sub-client | Methods |
|---|---|
client.assets |
list, listIndices |
client.products |
list, getBySymbol, tickers, ticker |
client.marketData |
orderbook, recentTrades, candles, sparklines, settlementPrice |
client.orders |
create, get, getByClientId, list, edit, cancel, cancelAll, batchCreate, batchEdit, batchDelete, createBracket, editBracket, getLeverage, setLeverage |
client.positions |
list, listMargined, setMarginMode |
client.history |
orders, fills, fillsDownload |
client.wallet |
balances, transactions, transactionsDownload, transfer, transferHistory |
client.account |
getUser, getPreferences, updatePreferences, listSubaccounts, getRateLimitQuota |
client.stats |
volume |
client.mmp |
config, reset |
client.heartbeat |
create, ack, list |
Public (no auth): v2/ticker, l2_orderbook, recent_trade, all_trades, mark_price, spot_price, funding_rate, product_updates, announcements
Private (auto-authenticated): orders, positions, margins, fills
import {
DeltaError, AuthenticationError, RateLimitError,
APIError, WebSocketError, TimeoutError, ValidationError,
} from 'delta-india-sdk';
try {
await client.orders.create({ /* ... */ });
} catch (err) {
if (err instanceof AuthenticationError) {
// 401/403 — bad keys or stale system clock
} else if (err instanceof RateLimitError) {
console.log(`retry after ${err.retryAfter}s`);
} else if (err instanceof TimeoutError) {
// request exceeded the configured timeout
} else if (err instanceof APIError) {
console.log(`API ${err.statusCode}: ${err.errorCode ?? err.message}`);
}
}| Environment | REST | WebSocket |
|---|---|---|
| Production | https://api.india.delta.exchange |
wss://socket.india.delta.exchange |
| Testnet | https://cdn-ind.testnet.deltaex.org |
wss://socket-ind.testnet.deltaex.org |
testnet: true selects the testnet pair automatically. Override either with baseUrl / wsUrl.
| Symptom | Likely cause | Fix |
|---|---|---|
AuthenticationError: invalid signature |
System clock drift > a few seconds | Sync NTP / system clock |
AuthenticationError on every authed call |
Wrong key/secret or wrong env (test vs prod) | Verify key matches the env you're hitting |
recent_trade listener never fires |
Subscription succeeded but no live trades | Subscribe to a liquid symbol like BTCUSD; messages are re-emitted from all_trades automatically |
tickers() returns thousands of rows |
Unfiltered call | Pass contract_types, underlying_asset_symbols, and/or expiry_date |
RateLimitError under load |
Burst exceeded sliding window | Use rateLimitMode: 'auto' (default) so calls queue instead of throwing |
The examples/ folder is ordered so you can read it top-to-bottom; each file is a self-contained runnable script. Public examples (01–02, 10) need no API key; the rest read DELTA_API_KEY and DELTA_API_SECRET from the environment.
| # | File | What it covers |
|---|---|---|
| 01 | 01-basic-market-data.ts |
products, tickers, orderbook, recent trades, candles, sparklines, assets |
| 02 | 02-options-chain.ts |
filtered tickers, greeks, ATM strike selection |
| 03 | 03-place-order.ts |
create / edit / cancel a limit order |
| 04 | 04-bracket-order.ts |
bracket order with stop trigger method, edit bracket, set leverage |
| 05 | 05-batch-orders.ts |
batch create / edit / delete (correct product_id body shape) |
| 06 | 06-positions.ts |
list with filters, computed unrealized PnL using num() |
| 07 | 07-wallet.ts |
balances, transactions, transfer, transfer history |
| 08 | 08-account.ts |
profile, preferences, subaccounts, rate-limit quota |
| 09 | 09-history.ts |
order history, fills, computed fees |
| 10 | 10-websocket-public.ts |
ticker, l2 orderbook, recent_trade, mark_price |
| 11 | 11-websocket-private.ts |
orders, positions, fills, margins (auto-auth) |
| 12 | 12-error-handling.ts |
each error class, narrowing with instanceof |
Run any of them with tsx:
npx tsx examples/01-basic-market-data.ts
DELTA_API_KEY=... DELTA_API_SECRET=... npx tsx examples/03-place-order.tsnpm install
npm run typecheck # tsc --noEmit
npm test # vitest run
npm run build # tsup -> dist/MIT