TypeScript-first Node.js SDK for Shark Exchange REST and Socket.IO APIs.
This SDK is designed to be used as a complete trading integration layer, not as a thin API wrapper. It gives you typed resource clients, request signing, rate limiting, retry handling, validation, order builders, websocket helpers, listen-key management, and examples for common workflows.
Source reference: Shark API Reference Documentation.
- Node.js 20 or newer.
- Node.js 22 or newer is recommended for current production apps.
- Shark API key and secret are required for authenticated account, order, wallet, position, and private stream APIs.
- Public market REST and public market streams do not require credentials.
The package ships ESM, CommonJS, and TypeScript declarations.
npm install shark-exchange-sdkESM or TypeScript:
import { SharkExchange } from "shark-exchange-sdk";CommonJS:
const { SharkExchange } = require("shark-exchange-sdk");import { SharkExchange } from "shark-exchange-sdk";
export const shark = new SharkExchange({
apiKey: process.env.SHARK_API_KEY,
apiSecret: process.env.SHARK_API_SECRET,
});Advanced setup:
const shark = new SharkExchange({
apiKey: process.env.SHARK_API_KEY,
apiSecret: process.env.SHARK_API_SECRET,
timeoutMs: 30_000,
retry: {
retries: 2,
baseDelayMs: 250,
maxDelayMs: 2_000,
},
rateLimit: {
enabled: true,
},
userAgent: "my-trading-app/1.0.0",
});SHARK_API_KEY=your-api-key
SHARK_API_SECRET=your-api-secretKeep live trading controls in your application, not inside examples. The examples that place or cancel orders are intentionally guarded with RUN_LIVE_TRADING=true.
- HMAC-SHA256 signing for authenticated REST endpoints.
- Timestamps for authenticated REST requests.
- Public vs authenticated endpoint routing.
- Endpoint-aware rate limiting based on Shark documentation.
- Retry and timeout behavior.
- Runtime validation for common order mistakes.
- Fluent order builders.
- Typed REST resources grouped by workflow.
- Socket.IO public and authenticated stream helpers.
- Listen-key creation, keepalive, and deletion.
- Typed resource response unwrapping for
{ data: ... }and{ result: ... }envelopes. - Raw request escape hatch for newly added or undocumented endpoints.
Public REST data:
await shark.market.ticker24Hr("BTCINR");
await shark.market.aggregateTrade("BTCINR");
await shark.market.depth("BTCINR");
await shark.market.klines({
pair: "BTCINR",
interval: "1m",
priceType: "MARK_PRICE",
limit: 100,
});Exchange metadata and account-level preferences:
await shark.exchange.info({ market: "INR" });
await shark.exchange.updatePreference({
contractName: "BTCINR",
marginMode: "ISOLATED",
leverage: 10,
});
await shark.exchange.updateLeverage({
contractName: "BTCINR",
leverage: 10,
});Order placement, lookup, editing, margin, and cancellation:
await shark.orders.placeMarket({
placeType: "ORDER_FORM",
quantity: 0.002,
side: "BUY",
symbol: "BTCINR",
reduceOnly: false,
marginAsset: "INR",
});
await shark.orders.placeLimit({
placeType: "ORDER_FORM",
quantity: 0.002,
price: 5_000_000,
side: "BUY",
symbol: "BTCINR",
reduceOnly: false,
marginAsset: "INR",
});
await shark.orders.edit({
clientOrderId: "client-order-id",
quantity: 0.003,
price: 5_050_000,
});
await shark.orders.open({ symbol: "BTCINR", pageSize: 50, sortOrder: "desc" });
await shark.orders.history({ symbol: "BTCINR", pageSize: 50, sortOrder: "desc" });
await shark.orders.get("client-order-id");
await shark.orders.getMultiple(["client-order-id-1", "client-order-id-2"]);
await shark.orders.linked("link-id");
await shark.orders.referenced("ref-id");
await shark.orders.splitTpSl({
positionId: "position-id",
splitTakeProfitOrders: [{ quantity: 0.001, price: 5_500_000 }],
splitStopLossOrders: [{ quantity: 0.001, price: 4_750_000 }],
});
await shark.orders.marginHistory({ symbol: "BTCINR" });
await shark.orders.delete("client-order-id");
await shark.orders.cancelAll();Use the builder when you want to avoid assembling payloads by hand:
const payload = shark
.order()
.symbol("BTCINR")
.buy()
.marginAsset("INR")
.limit(0.002, 5_000_000)
.takeProfit(5_500_000)
.stopLoss(4_750_000)
.build();
await shark.orders.place(payload);Stop orders:
const stopLimit = shark
.order()
.symbol("BTCINR")
.sell()
.marginAsset("INR")
.stopLimit(0.002, 4_900_000, 4_890_000)
.reduceOnly()
.build();await shark.positions.list("OPEN", { symbol: "BTCINR", pageSize: 100 });
await shark.positions.get("position-id");
await shark.positions.closeAll();await shark.wallet.futures({ marginAsset: "INR" });
await shark.wallet.funding({ marginAsset: "INR" });await shark.userData.tradeHistory({
symbol: "BTCINR",
pageSize: 100,
sortOrder: "desc",
});
await shark.userData.transactionHistory({
symbol: "BTCINR",
positionId: "position-id",
pageSize: 100,
});const { listenKey } = await shark.listenKeys.create();
await shark.listenKeys.keepAlive();
await shark.listenKeys.delete();Shark realtime uses Socket.IO. The SDK uses socket.io-client for the correct protocol.
import { SharkExchange, streamTopics } from "shark-exchange-sdk";
const shark = new SharkExchange();
const stream = shark.streams.public({ autoConnect: false });
stream.on("connect", () => {
stream.subscribe([
streamTopics.depth("BTCINR", "0.1"),
streamTopics.kline("BTCINR", "1m"),
streamTopics.markPrice("BTCINR"),
streamTopics.aggregateTrade("BTCINR"),
streamTopics.ticker("BTCINR"),
streamTopics.marketInfo("BTCINR"),
streamTopics.markPriceArray(),
streamTopics.tickerArray(),
streamTopics.allContractDetails(),
]);
});
stream.on("depthUpdate", console.log);
stream.on("kline", console.log);
stream.on("markPriceUpdate", console.log);
stream.on("aggTrade", console.log);
stream.on("24hrTicker", console.log);
stream.on("marketInfo", console.log);
stream.connect();For bots and dashboards, use REST for the initial snapshot and public streams for updates:
const symbols = ["BTCINR", "ETHINR"];
const snapshots = new Map<string, { lastPrice: number; bestBid: number; bestAsk: number }>();
for (const symbol of symbols) {
const [ticker, depth] = await Promise.all([
shark.market.ticker24Hr(symbol),
shark.market.depth(symbol),
]);
snapshots.set(symbol, {
lastPrice: Number(ticker.c),
bestBid: Number(depth.b?.[0]?.[0]),
bestAsk: Number(depth.a?.[0]?.[0]),
});
}
const stream = shark.streams.public({ autoConnect: false });
stream.on("connect", () => {
stream.subscribe(symbols.flatMap((symbol) => [
streamTopics.ticker(symbol),
streamTopics.markPrice(symbol),
streamTopics.depth(symbol, "0.1"),
]));
});
stream.on("24hrTicker", console.log);
stream.on("markPriceUpdate", console.log);
stream.on("depthUpdate", console.log);
stream.connect();For a complete live-data cache with depth grouping, staleness checks, reconnect resubscription, and graceful shutdown, see examples/08-live-data-feed.ts.
Managed authenticated streams create a listen key and refresh it automatically:
const stream = await shark.streams.managedAuthenticated({
autoConnect: false,
autoKeepAlive: true,
keepAliveIntervalMs: 30 * 60_000,
});
stream.on("orderFilled", console.log);
stream.on("orderPartiallyFilled", console.log);
stream.on("orderCancelled", console.log);
stream.on("orderFailed", console.log);
stream.on("newOrder", console.log);
stream.on("updateOrder", console.log);
stream.on("newPosition", console.log);
stream.on("updatePosition", console.log);
stream.on("closePosition", console.log);
stream.on("balanceUpdate", console.log);
stream.on("newTrade", console.log);
stream.on("sessionExpired", console.log);
stream.connect();If you already have a listen key:
const stream = shark.streams.authenticated("existing-listen-key");Typed resource methods return the useful payload directly.
For example, if Shark returns:
{
"data": {
"s": "BTCINR",
"c": "5271387.66"
}
}Then:
const ticker = await shark.market.ticker24Hr("BTCINR");
console.log(ticker.s);Use raw.request() when you need the exact server response:
const raw = await shark.raw.request("GET", "/v1/market/ticker24Hr/BTCINR", {
auth: false,
});The Shark docs currently show both path-style and query-style examples for a few public market endpoints. The SDK defaults to the documented JavaScript path style and keeps query mode available.
await shark.market.ticker24Hr("BTCINR");
await shark.market.ticker24Hr("BTCINR", { mode: "query" });
await shark.market.depth("BTCINR");
await shark.market.depth("BTCINR", { mode: "query" });
await shark.market.aggregateTrade("BTCINR");
await shark.market.aggregateTrade("BTCINR", { mode: "query" });Order lookup methods also expose compatibility modes where the docs differ. Live testing showed single-order lookup should use query mode by default; path mode remains available when you need to match a path-style integration.
await shark.orders.linked("link-id", { mode: "query" });
await shark.orders.referenced("ref-id", { mode: "path" });
await shark.orders.get("client-order-id");
await shark.orders.get("client-order-id", { mode: "path" });import {
SharkApiError,
SharkAuthError,
SharkTimeoutError,
SharkValidationError,
} from "shark-exchange-sdk";
try {
await shark.orders.placeMarket({
placeType: "ORDER_FORM",
quantity: 0,
side: "BUY",
symbol: "BTCINR",
reduceOnly: false,
marginAsset: "INR",
});
} catch (error) {
if (error instanceof SharkValidationError) {
console.error("Bad SDK input:", error.message);
} else if (error instanceof SharkAuthError) {
console.error("Missing or invalid local credentials:", error.message);
} else if (error instanceof SharkTimeoutError) {
console.error("Request timed out:", error.message);
} else if (error instanceof SharkApiError) {
console.error("Shark API error:", error.status, error.response);
} else {
throw error;
}
}Some Shark endpoints expose cursor-like timestamps. The SDK provides a generic helper so your app can keep its resource call readable.
for await (const order of shark.paginate((cursor) =>
shark.orders.history({
symbol: "BTCINR",
pageSize: 100,
endTimestamp: cursor,
})
)) {
console.log(order.clientOrderId);
}If the API shape differs, pass getItems and getNextTimestamp.
The examples/ directory is designed as a user guide in code:
examples/01-public-market-data.ts: public market REST data.examples/02-orders-and-builders.ts: market, limit, stop, edit, lookup, margin, and cancel flows.examples/03-account-positions-wallet.ts: positions, wallet, user-data, exchange preferences.examples/04-public-stream.ts: public Socket.IO market streams.examples/05-authenticated-stream.ts: listen-key authenticated account stream.examples/06-configuration-and-errors.ts: retry, timeout, rate limit, errors, abort signals.examples/07-commonjs.js: CommonJS usage.examples/08-live-data-feed.ts: REST bootstrap plus live Socket.IO market updates for bots and dashboards.
Run TypeScript examples with a runner such as tsx:
npx tsx examples/01-public-market-data.tsRun CommonJS examples with Node:
node examples/07-commonjs.jsnpm run typecheck
npm test
npm run buildnpm run typecheck
npm test
npm run build
npm pack --dry-runIf your local npm cache has permission issues, use a temporary cache:
npm pack --dry-run --cache /private/tmp/npm-cache