API documentation
Build trading bots and integrations on the same engine the website uses. All endpoints live under https://zarkoin.com/api/v1. Requests and responses are JSON. Decimal values are strings, to keep full precision.
Overview
Public endpoints (market data) need no key. Private endpoints accept an API key you create in Account, API keys. Each key has permissions: READ (balances, orders, positions), SPOT (place and cancel spot orders) and FUTURES (futures trading). Keys can never withdraw funds. Restrict keys to your server’s IP addresses whenever you can.
Symbols are written without a separator, e.g. BTCUSDT. Timestamps are Unix milliseconds.
Authentication
Send three headers with every private request:
X-ZK-APIKEY: your API key
X-ZK-TIMESTAMP: current Unix time in milliseconds
X-ZK-SIGNATURE: hex( HMAC_SHA256( secret, timestamp + METHOD + pathWithQuery + body ) )pathWithQuery is the full path including /api/v1 and the query string, for example /api/v1/spot/orders/open?symbol=BTCUSDT. body is the exact JSON you send, serialised compactly with no spaces (empty string for GET and DELETE without a body). The timestamp must be within 10 seconds of our server time.
Rate limits and errors
Each IP can make 600 requests per minute. Placing orders is limited to 60 per 10 seconds per account. Exceeding a limit returns HTTP 429. Errors always have the same shape:
{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "Insufficient USDT balance" } }Market data
| Method | Path | Description | Key permission |
|---|---|---|---|
| GET | /market/markets | Spot markets with tick size, lot size, minimums and fees | Public |
| GET | /market/tickers | 24-hour statistics for every spot market | Public |
| GET | /market/ticker?symbol=BTCUSDT | 24-hour statistics for one market | Public |
| GET | /market/depth?symbol=BTCUSDT&limit=50 | Order book snapshot | Public |
| GET | /market/trades?symbol=BTCUSDT&limit=50 | Recent public trades | Public |
| GET | /market/klines?symbol=BTCUSDT&interval=1h&limit=500&endTime= | Candles: 1m 5m 15m 30m 1h 4h 1d 1w | Public |
| GET | /futures/markets | Perpetual contracts, leverage limits and margin tiers | Public |
| GET | /futures/tickers | Futures 24-hour statistics with mark price and funding | Public |
| GET | /futures/mark?symbol=BTCUSDT | Mark price, index price, funding rate and next funding time | Public |
| GET | /futures/funding-history?symbol=BTCUSDT | Past funding rates | Public |
Spot trading
| Method | Path | Description | Key permission |
|---|---|---|---|
| POST | /spot/preview | All-in cost preview: fee, average and worst price, what you receive | none |
| POST | /spot/orders | Place an order | SPOT |
| POST | /spot/orders/oco | Place a one-cancels-the-other pair (take profit + stop loss) | SPOT |
| DELETE | /spot/orders/:id | Cancel one order (cancels both legs of an OCO) | SPOT |
| DELETE | /spot/orders?symbol= | Cancel all open orders (optionally for one market) | SPOT |
| GET | /spot/orders/open?symbol= | Open orders | READ |
| GET | /spot/orders/history?symbol=&page= | Order history | READ |
| GET | /spot/orders/:id | One order with its fills | READ |
| GET | /spot/trades?symbol=&page= | Your fills | READ |
Order body:
{
"symbol": "BTCUSDT",
"side": "BUY" | "SELL",
"type": "LIMIT" | "MARKET" | "STOP_LIMIT" | "STOP_MARKET" | "TAKE_PROFIT_LIMIT" | "TAKE_PROFIT_MARKET"
| "TRAILING_STOP_MARKET",
"timeInForce": "GTC" | "IOC" | "FOK" | "POST_ONLY", // limit orders, default GTC
"price": "60000", // limit price
"stopPrice": "59000", // stop / take-profit trigger
"quantity": "0.001", // base amount
"quoteQuantity": "100", // market buy by quote amount (instead of quantity)
"callbackRate": "1.5", // trailing stop: distance in percent, 0.1–10
"activationPrice": "62000",// trailing stop: optional, SELL above / BUY below the market
"clientOrderId": "my-id-1" // optional, letters, digits, - and _
}A trailing stop tracks the highest price (sell) or lowest price (buy) after activation and executes at market when the price turns back by callbackRate. While it waits, stopPrice shows the current trigger and trailPrice the best price so far.
OCO body. SELL: price above the market and stopPrice below it; BUY: the reverse. Funds are locked once for both legs:
{
"symbol": "BTCUSDT",
"side": "SELL",
"quantity": "0.5",
"price": "66000", // take-profit limit leg (rests on the book)
"stopPrice": "58000", // stop-loss trigger
"stopLimitPrice": "57900", // optional; omit for a stop-market leg
"clientOrderId": "my-oco-1"
}
// → { "ocoGroup": "…", "orders": [limitLeg, stopLeg] }
// A fill on the limit leg cancels the stop leg; the stop triggering cancels the limit leg.Futures trading
| Method | Path | Description | Key permission |
|---|---|---|---|
| GET | /futures/account | Futures wallet: balance, margin, available | READ |
| GET | /futures/positions | Open positions with liquidation price | READ |
| POST | /futures/leverage | Set leverage { symbol, leverage } | FUTURES |
| POST | /futures/margin-mode | Set CROSS or ISOLATED { symbol, marginMode } | FUTURES |
| POST | /futures/orders | Place an order | FUTURES |
| DELETE | /futures/orders/:id | Cancel one order | FUTURES |
| GET | /futures/orders/open | Open orders | READ |
| GET | /futures/orders/history | Order history | READ |
| POST | /futures/positions/tpsl | Set take-profit / stop-loss on a position | FUTURES |
| POST | /futures/positions/close | Close a position at market | FUTURES |
| POST | /futures/positions/close-all | Close every position at market | FUTURES |
| GET | /futures/positions/history | Closed positions with realised PnL | READ |
| GET | /futures/funding-payments | Funding you paid or received | READ |
Order body adds futures fields to the spot shape:
{
"symbol": "BTCUSDT", "side": "BUY", "type": "LIMIT", "price": "60000", "quantity": "0.01",
"reduceOnly": false, // only reduce an existing position
"triggerBy": "MARK_PRICE", // or LAST_PRICE, for stop orders
"tpPrice": "66000", // optional take-profit attached to the order
"slPrice": "58000", // optional stop-loss attached to the order
"callbackRate": "1.5", // type TRAILING_STOP_MARKET: distance in percent, 0.1–10
"activationPrice": "64000" // trailing stop: optional, SELL above / BUY below the trigger price
}WebSocket
Connect to wss://zarkoin.com/ws and subscribe to channels. You receive a snapshot immediately and then updates. Messages look like { ch, data, ts }.
| Method | Path | Description | Key permission |
|---|---|---|---|
| SUB | tickers | All spot tickers, about once a second | Public |
| SUB | ticker:BTCUSDT | One spot ticker | Public |
| SUB | depth:BTCUSDT | Top 50 order book levels | Public |
| SUB | trades:BTCUSDT | New public trades | Public |
| SUB | kline:BTCUSDT:1m | The current candle | Public |
| SUB | f.ticker:BTCUSDT f.depth:… f.trades:… f.kline:… | Futures equivalents | Public |
| SUB | f.mark:BTCUSDT | Mark price, index price and funding rate | Public |
After authenticating, private events arrive automatically on these channels: orders, f.orders, positions, balances, notifications, deposits, withdrawals.
const ws = new WebSocket('wss://zarkoin.com/ws');
ws.onopen = () => {
ws.send(JSON.stringify({ op: 'subscribe', channels: ['ticker:BTCUSDT', 'depth:BTCUSDT', 'trades:BTCUSDT', 'kline:BTCUSDT:1m'] }));
// private events (orders, balances, positions…) need a logged-in session token:
// ws.send(JSON.stringify({ op: 'auth', token: ACCESS_TOKEN }));
setInterval(() => ws.send(JSON.stringify({ op: 'ping' })), 20000);
};
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.ch) console.log(msg.ch, msg.data); // { ch, data, ts }
};Code examples
Node.js (18+):
import crypto from 'node:crypto';
const API_KEY = process.env.ZK_API_KEY;
const API_SECRET = process.env.ZK_API_SECRET;
const BASE = 'https://zarkoin.com';
async function zk(method, path, body) {
const ts = Date.now().toString();
const payload = body ? JSON.stringify(body) : ''; // compact JSON, no spaces
const signature = crypto
.createHmac('sha256', API_SECRET)
.update(ts + method + path + payload)
.digest('hex');
const res = await fetch(BASE + path, {
method,
headers: {
'Content-Type': 'application/json',
'X-ZK-APIKEY': API_KEY,
'X-ZK-TIMESTAMP': ts,
'X-ZK-SIGNATURE': signature,
},
body: payload || undefined,
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
return data;
}
// Place a limit buy for 0.001 BTC at 60,000 USDT
const order = await zk('POST', '/api/v1/spot/orders', {
symbol: 'BTCUSDT', side: 'BUY', type: 'LIMIT', price: '60000', quantity: '0.001', timeInForce: 'GTC',
});
console.log(order);
// List open orders
console.log(await zk('GET', '/api/v1/spot/orders/open?symbol=BTCUSDT'));Python 3:
import hashlib, hmac, json, os, time
import requests
API_KEY = os.environ["ZK_API_KEY"]
API_SECRET = os.environ["ZK_API_SECRET"].encode()
BASE = "https://zarkoin.com"
def zk(method, path, body=None):
ts = str(int(time.time() * 1000))
payload = json.dumps(body, separators=(",", ":")) if body is not None else "" # compact JSON
sig = hmac.new(API_SECRET, (ts + method + path + payload).encode(), hashlib.sha256).hexdigest()
headers = {
"Content-Type": "application/json",
"X-ZK-APIKEY": API_KEY,
"X-ZK-TIMESTAMP": ts,
"X-ZK-SIGNATURE": sig,
}
r = requests.request(method, BASE + path, headers=headers, data=payload or None, timeout=10)
data = r.json()
if not r.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
return data
# Open a 10x long of 0.01 BTC with a stop-loss
print(zk("POST", "/api/v1/futures/leverage", {"symbol": "BTCUSDT", "leverage": 10}))
print(zk("POST", "/api/v1/futures/orders", {
"symbol": "BTCUSDT", "side": "BUY", "type": "MARKET", "quantity": "0.01", "slPrice": "58000",
}))