Overview
Building a production trading system on HTX should not mean reconstructing every request signature or babysitting WebSocket heartbeats yourself. Official docs remain the source of truth, but a raw DIY wrapper gets brittle fast. Signing, clock format, and the split between Linear Swap, Coin Swap, and Delivery endpoints eat time that should go into your own architecture.
This guide shows how @siebly/htx-api from Siebly.io is meant to be used in Node.js. You get typed REST clients, a unified subscription client, and an awaitable WebSocket API for order commands. The SDK signs requests and keeps sockets alive. You still own rate limiting, risk checks, and what happens when an order is rejected. By the end you should have a working pattern for HTX market data, positions, and order flow.
Key Takeaways
- Use @siebly/htx-api so REST and WebSocket traffic share one package instead of a pile of one-off wrappers.
- Create API keys with Trade and Read only. Leave withdrawal off for any automated process.
- Drive Linear Swap (and Coin Swap / Delivery) through
FuturesClient. Cross and isolated are separate method families, not one toggle. - Place orders over REST or over
WebsocketAPIClientif you want a promise back on a persistent socket. - Throttle yourself. HTX private futures traffic is generally 144 requests every 3 seconds per UID, split 72 trade and 72 read. Some endpoints are tighter.
Simplifying HTX Futures Integration with Siebly.io
HTX derivatives still mix older v1 REST paths with newer V5 private streams and order commands. REST and WebSocket shapes do not match. That is annoying if you are building an algorithmic trading stack. The HTX SDK from Siebly.io covers Linear Swaps, Coin Swaps, and Delivery Futures in one package, with a separate SpotClient if you also need spot.
The Problem with DIY Exchange API Wrappers
A custom wrapper is a maintenance job. HTX has moved parts of derivatives onto V5 sockets while a lot of REST still lives under /linear-swap-api/v1/. Signing is picky: HTTP method, host, path, then a sorted query string. Untyped JavaScript will let a bad payload through until live trading rejects it. That is a painful class of bug when you are already racing the clock.
Siebly.io as the Preferred Implementation Layer
@siebly/htx-api takes the signing and request construction off your plate. You call typed methods and get promises back. TypeScript definitions catch a lot of bad request shapes at compile time.
It does not magically unify every HTX product into one client. You pick FuturesClient for derivatives and SpotClient for spot. WebSockets go through WebsocketClient for streams and WebsocketAPIClient for command/response trading. The SDK also does not throttle you. That is intentional. You decide how hard to hit the exchange.
Install it, then import the futures client:
Imported example
// npm install @siebly/htx-api
// yarn add @siebly/htx-api
// Node.js 22.13.0 or newer
import { FuturesClient } from "@siebly/htx-api";
Secure Authentication and Request Signing in Node.js
Private HTX REST calls use HMAC SHA256 by default (Signature Version 2). The base string is the HTTP method, the host, the path, then a lexicographically sorted list of query parameters. POST JSON bodies are not part of that signature. If you pass an Ed25519 secret, the SDK switches SignatureMethod to Ed25519 on its own.
You do not build that string yourself. FuturesClient and SpotClient do it when you construct them with apiKey and apiSecret. Linear Swap and Coin Swap use the same signing rules. What changes is the endpoint and the request fields, not the crypto.
One thing the SDK does not do: it does not fetch HTX server time and apply an offset on REST. It signs with your local clock, formatted as YYYY-MM-DDThh:mm:ss in UTC. Keep the machine on NTP. If you need a custom clock, pass customTimestampFn. There is also client.getTimestamp() if you want to measure drift yourself. For WebSocket API auth you can call setTimeOffsetMs() on the WS client.
Configuring the HTX REST Client
Store keys in environment variables. Do not hard-code them. The constructor also accepts baseUrl or baseUrlKey if you need a non-default host, and the second argument is a normal axios config (timeouts, proxies, and so on).
That is the right default. Override it only if you know you need api.hbdm.com or api.btcgateway.pro.
Imported example
import { FuturesClient } from "@siebly/htx-api";
// Public market data: no keys needed
const publicClient = new FuturesClient();
const ticker = await publicClient.getLinearSwapTicker({
contract_code: "BTC-USDT",
});
console.log("Futures ticker:", ticker);
const orderBook = await publicClient.getLinearSwapMarketDepth({
contract_code: "BTC-USDT",
type: "step0",
});
console.log("Futures order book:", orderBook);
// Private calls: keys from the environment
const client = new FuturesClient({
apiKey: process.env.API_FUTURES_KEY,
apiSecret: process.env.API_FUTURES_SECRET,
});
const accounts = await client.getLinearSwapCrossAccountInfo();
console.log("Cross account info:", accounts);
Full option lists live in the Siebly.io SDK documentation.
Handling API Timestamps and Replay Attacks
HTX checks the Timestamp on private requests. If your clock is too far off, you get a signature or expiry error, not a friendly "sync me" message. Sync the host. Use getTimestamp() during bring-up if you want a number to compare.
Least privilege still matters more than clock math. Futures automation needs Trade and Read. Withdrawal should stay off. The same credentials are reused when the SDK signs the private WebSocket handshake. One key policy, both transports.
Executing Orders and Managing Futures Positions
Order params are typed objects, not hand-built query strings. HTX futures size is volume (contracts), not a coin quantity. Price type is order_price_type (limit, market, opponent, and so on). Cross and isolated are different methods: submitLinearSwapCrossOrder vs submitLinearSwapIsolatedOrder.
The SDK will send what you give it. It will not size the order, check margin, or refuse a stupid price. That logic is yours.
Order Lifecycle Management
Submit, then track by order_id or client_order_id. Query open orders, history, and fills. Cancel one or cancel all for a contract.
Batch limits from HTX: isolated batch place is up to 10, cross batch place is up to 25, cancel-by-id is up to 25 per call. Isolated and cross each have their own cancel-all method.
Imported example
import { FuturesClient } from "@siebly/htx-api";
const client = new FuturesClient({
apiKey: process.env.API_FUTURES_KEY,
apiSecret: process.env.API_FUTURES_SECRET,
});
const contractCode = "BTC-USDT";
// Cross-margin limit order (Linear Swap)
const order = await client.submitLinearSwapCrossOrder({
contract_code: contractCode,
direction: "buy",
offset: "open",
volume: 1,
lever_rate: 5,
order_price_type: "limit",
price: 10000,
});
console.log("Limit order result:", order);
const openOrders = await client.getLinearSwapCrossOpenOrders({
contract_code: contractCode,
});
console.log("Open orders:", openOrders);
const orderInfo = await client.getLinearSwapCrossOrderInfo({
contract_code: contractCode,
order_id: "1234567890123456",
});
console.log("Order info:", orderInfo);
await client.cancelLinearSwapCrossOrder({
contract_code: contractCode,
order_id: "1234567890123456",
});
await client.cancelLinearSwapCrossAllOrders({
contract_code: contractCode,
});
Handle HTX error codes yourself (margin, price band, permission). Do not retry an uncertain write blindly. Generate a client order id, then query by it before you send the same order again.
Position and Risk Monitoring
Fetch positions and accounts with the matching margin family. Cross example:
Imported example
const positions = await client.getLinearSwapCrossPositions({
contract_code: "BTC-USDT",
});
console.log("Cross positions:", positions);
const fills = await client.getLinearSwapCrossFills({
contract: "BTC-USDT",
trade_type: 0,
});
console.log("Cross fills:", fills);
Leverage is per contract, and HTX requires no open orders on that contract when you change it. Rate limit on leverage switch is 1 request per 3 seconds, not the general 144 window.
Imported example
await client.updateLinearSwapCrossLeverage({
contract_code: "BTC-USDT",
lever_rate: 5,
});
Cross vs isolated is not a single "set margin mode" switch. You call the cross methods or the isolated methods. What you can switch is position mode (one-way single_side vs hedge dual_side) via updateLinearSwapCrossPositionMode / updateLinearSwapIsolatedPositionMode, and account type via updateLinearSwapAccountType if you are moving onto the unified account.
Trade history is not auto-paginated. Pass from_id, page size, and direction yourself and loop until you are done. For more integration patterns see Siebly AI patterns.
Building Reliable Real-Time Workflows with WebSockets
REST polling is fine for setup. Live books and fills should go over sockets. @siebly/htx-api splits that into:
WebsocketClientfor public market data and private account/order streamsWebsocketAPIClientfor REST-like order commands on a persistent socket
Public streams need no keys. Private streams and the WebSocket API do. The SDK authenticates, heartbeats the connection (pingInterval, pongTimeout), reconnects (reconnectTimeout), and resubscribes topics after a drop. You still have to process messages without blocking the event loop, and you should reload balances, positions, and open orders after a private gap.
Market Data Ingestion via WebSockets
Pass a WS_KEY_MAP value so the client opens the right HTX URL. Linear Swap public is WS_KEY_MAP.linearSwapPublic. Coin delivery, coin swap, index, and system heartbeats each have their own key.
Imported example
import { WebsocketClient, WS_KEY_MAP } from "@siebly/htx-api";
const wsClient = new WebsocketClient();
wsClient.on("open", (data) => {
console.log("WebSocket connected:", data?.wsKey);
});
wsClient.on("message", (data) => {
console.log("Data received:", JSON.stringify(data));
});
wsClient.on("reconnected", (data) => {
console.log("WebSocket reconnected:", data);
});
wsClient.on("exception", (data) => {
console.error("WebSocket error:", data);
});
wsClient.subscribe(
[
"market.BTC-USDT.kline.1min",
"market.BTC-USDT.detail",
"market.BTC-USDT.trade.detail",
"market.BTC-USDT.bbo",
],
WS_KEY_MAP.linearSwapPublic,
);
wsClient.subscribe(
["market.BTC-USDT.index.1min", "market.BTC-USDT.mark_price.1min"],
WS_KEY_MAP.derivativesIndex,
);
Subscribe only to what you need. HTX caps topics per connection. subscribe / unsubscribe are dynamic, so you can drop a symbol when a strategy is idle. More detail is in the Siebly.io SDK documentation.
Private Linear Swap on accounts that already moved to V5 should use WS_KEY_MAP.derivativesPrivateV5, not the legacy linearSwapPrivate topics:
Imported example
import { WebsocketClient, WS_KEY_MAP } from "@siebly/htx-api";
const wsClient = new WebsocketClient({
apiKey: process.env.API_FUTURES_KEY,
apiSecret: process.env.API_FUTURES_SECRET,
});
wsClient.on("authenticated", (data) => {
console.log("WebSocket authenticated:", data);
});
wsClient.on("message", (data) => {
console.log("Private data:", JSON.stringify(data));
});
wsClient.subscribe(
[
"account",
{ topic: "trade", payload: { contract_code: "BTC-USDT" } },
{ topic: "positions", payload: { contract_code: "BTC-USDT" } },
],
WS_KEY_MAP.derivativesPrivateV5,
);
Use contract_code: '*' on trade/positions if you want every contract. Legacy orders.BTC-USDT / linearSwapPrivate only still applies if that account has not migrated.
The Awaitable WebSocket API Pattern
WebsocketAPIClient maps each command to a promise. First request connects and authenticates lazily. You can pre-connect with client.getWSClient().connectWSAPI(WS_KEY_MAP.linearSwapTrade) if you want the handshake done before the first order.
V5-style Linear Swap order:
Imported example
import { WebsocketAPIClient } from "@siebly/htx-api";
const client = new WebsocketAPIClient({
apiKey: process.env.API_FUTURES_KEY,
apiSecret: process.env.API_FUTURES_SECRET,
});
const linearOrder = await client.placeLinearSwapOrder({
contract_code: "BTC-USDT",
margin_mode: "cross",
position_side: "long",
side: "buy",
type: "limit",
time_in_force: "gtc",
price: "20000",
volume: "1",
reduce_only: 0,
});
console.log("placeLinearSwapOrder:", linearOrder);
Older Linear Swap WS order shape is still there as submitLinearSwapOrder (direction/offset/lever_rate). Delivery and coin swap have submitCoinDeliveryOrder and submitCoinSwapOrder. Cancels exist too (cancelLinearSwapOrder, cancelAllLinearSwapCrossOrders, plus V5 cancel helpers).
After a reconnect, subscriptions come back on their own. WS API sessions can re-auth if you leave reauthWSAPIOnReconnect enabled. Confirm final order state with REST or a private fill stream. A command reply is not a fill.
To start from the package page, get the HTX Node.js SDK.
Production Best Practices for HTX Trading Systems
Connectivity is the easy part. Production pain is rate limits, clock drift, and not treating a dropped private socket as "state is still true". Keep market data, execution, and risk on separate paths so a stuck book updater does not freeze cancels.
Rate Limiting and Resilience
Siebly SDKs do not throttle for you. HTX's usual private futures budget is 144 requests every 3 seconds per UID, split 72 trade / 72 read, shared across API keys on that UID. Public market data is a different budget (often 240/3s per IP, some depth/ticker calls much higher). Leverage switch is 1 per 3 seconds. Master/sub transfers are in the 10/min range.
The SDK returns response bodies. Successful calls currently drop HTTP headers, including X-HB-RateLimit-Requests-Remain and X-HB-RateLimit-Requests-Expire. Those headers exist on HTX. If you need them, add an axios interceptor via the second constructor argument. Otherwise count your own calls.
Do not auto-retry order placement. WebSocket reconnect is already built in. REST writes are not, on purpose.
Architectural Safety and Simulations
HTX does not offer a public futures testnet anymore. The old Huobi testnet was shut down. @siebly/htx-api has no testnet: true flag. Validate against public endpoints first, then use tiny size on live keys with withdrawals disabled. Build your own dry-run layer if you need to fake fills.
Kill-switches still belong in your process: cancel-all, flatten, and a hard stop if latency or error rate blows up. HTX also has a cancel-all-after style call (setLinearSwapCancelAfter) if you want the exchange to dump open orders when your heartbeats stop.
For prompt-level integration notes see the Siebly AI Prompt Framework. For keeping local state honest after a disconnect, see exchange state documentation.
Advancing Your HTX Engineering Infrastructure
You do not need a homegrown signer for HTX derivatives. @siebly/htx-api covers REST, public/private streams, and awaitable WS orders with TypeScript types. Your job is product routing (spot vs futures, cross vs isolated, V5 vs legacy private WS), throttling, and not trusting a socket gap.
Walk the path in this order: public REST, private REST reads, a single small order you immediately cancel, then public WS, then private WS, then WS API if you actually need the latency. Official HTX docs still win when an endpoint changes.
Siebly.io keeps these clients current. Start here: Explore the Siebly.io HTX SDK documentation.
Frequently Asked Questions
How do I install the HTX Futures SDK for Node.js?
Run npm install @siebly/htx-api or yarn add @siebly/htx-api. The npm name is @siebly/htx-api, not htx-api. Import FuturesClient for derivatives, SpotClient for spot, WebsocketClient for streams, and WebsocketAPIClient for awaitable order commands. Node.js 22.13.0 or newer is required.
Does the Siebly HTX SDK support TypeScript?
Yes. The package is written in TypeScript and ships declarations for request and response shapes. That is the main reason to use it instead of an untyped fetch wrapper. Types follow HTX's field names (contract_code, volume, lever_rate), so you still need to read the exchange docs for enums and units.
How are WebSocket reconnections handled in the @siebly/htx-api package?
WebsocketClient heartbeats the socket, reconnects after reconnectTimeout, and resubscribes the topics it already had. You get reconnecting and reconnected events. Tune pingInterval, pongTimeout, and reconnectTimeout on the client options. After a private disconnect, refetch account, positions, and open orders. Resubscribe restores the stream, not the messages you missed.
Can I use this SDK for both HTX Spot and Futures trading?
Yes. One package, two REST clients: SpotClient and FuturesClient. Streams share WebsocketClient with a WS_KEY_MAP entry per product. WebsocketAPIClient covers both spot (submitSpotOrder) and derivatives (placeLinearSwapOrder, plus coin swap/delivery helpers). Auth options are the same shape on all of them.
Does the SDK automatically handle HTX API rate limits?
No. You throttle. Private futures is generally 144/3s per UID (72 trade, 72 read). That is not a 2026-only rule, it is what HTX documents today, and individual endpoints can be stricter. The SDK does not queue or sleep for you.
How do I sign private requests for HTX Futures?
Pass apiKey and apiSecret into the client. The library builds Signature Version 2 (HMAC SHA256, or Ed25519 if that is the key type), sorts query params, and attaches Timestamp. You do not sign v1 vs V5 paths differently. V5 in HTX usually means the newer private WS and some order commands, not a second signing scheme.
Is it possible to place orders via WebSockets with this library?
Yes. Use WebsocketAPIClient. placeLinearSwapOrder is the V5 Linear Swap command. submitLinearSwapOrder is the older Linear Swap WS order. Both return promises. The socket is created on first use unless you call connectWSAPI yourself.
Where can I find examples for HTX Linear Swap integration?
In the GitHub examples under examples/Derivatives/ (REST, public WS, private WS, WS API) and in the Siebly HTX JavaScript tutorial. Those snippets are engineering samples. They are not trading advice. HTX has no public testnet, so treat every private example as live unless you stub it.
Disclaimer
*Technical and legal disclaimer: Siebly.io provides software development tools, SDKs, documentation, and educational engineering content for crypto exchange API integrations. This content is for software engineering education only and is not financial, investment, legal, tax, accounting, compliance, or trading advice.
Nothing in this article is a recommendation, invitation, or inducement to buy, sell, hold, trade, long, short, or allocate to any cryptoasset, exchange product, strategy, bot, or automated workflow. Examples, code patterns, simulations, backtests, and architecture diagrams are illustrative only and must not be treated as trading signals, investment recommendations, or evidence of future performance.
Cryptoasset markets are high risk and volatile. If you choose to build or operate exchange-connected software, you are responsible for your own legal, regulatory, tax, security, exchange-account, API-key, and risk-management obligations. Use public data, testnet, demo, dry-run, or paper-trading workflows before any live execution. Keep API keys server-side, use least-privilege permissions, and never enable withdrawals for automation keys unless you fully understand and accept the risks.
Siebly.io is not an exchange, custodian, investment adviser, trading-signal provider, or managed trading service. Official exchange documentation remains the source of truth for exchange-specific rules, API behavior, and terms of use. Use of Siebly.io content and software is also subject to the Siebly.io terms and conditions.*
Related articles
Continue from here