Account
Balance, positions, exposure, transfer history, PnL.
Code samples assume the signed-request helper from Endpoints / Overview → Client setup.
GET /v2/futures/balance
Futures wallet balance.
| Auth | API_KEY (READ_ONLY+) |
| Rate limit | default 1000/min |
Query
subAccountId(optional, string) — Specific sub-account. Omit for the user's primary sub.
Response
{
"ok": true,
"balances": {
"assets": [
{
"asset": "USDT",
"walletBalance": "1234.56",
"availableBalance": "1100.45",
"marginBalance": "1234.56",
"unrealizedProfit": "12.34",
"crossWalletBalance": "1234.56"
}
]
}
}Errors — 403 Unauthorized subAccountId (sub does not belong to caller); 400 No sub account found.
Example
TS=$(($(date +%s) * 1000)); QUERY="timestamp=${TS}"
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/futures/balance?${QUERY}&signature=${SIG}"const balance = await signedRequest('GET', '/v2/futures/balance');
console.log(balance);balance = signed_request("GET", "/v2/futures/balance").json()
print(balance)body, _ := zdex.SignedRequest("GET", "/v2/futures/balance", nil, nil)
fmt.Println(string(body))let balance = signed_request(reqwest::Method::GET, "/v2/futures/balance", &[], None).await?;
println!("{balance}");$balance = signed_request('GET', '/v2/futures/balance');
print_r($balance);GET /v2/positions
Open positions for the authenticated account.
| Auth | API_KEY |
| Rate limit | default |
Query
status(optional) —OPEN(default) |CLOSED.
Response
{
"ok": true,
"positions": [
{
"id": "cmod...",
"accountId": "cmnw...",
"venuePositionId": "...",
"symbol": "BTCUSDT",
"quantity": "0.5",
"avgEntryPrice": "30000",
"markPrice": "30100",
"liquidationPrice": "27500",
"unrealizedPnl": "50",
"realizedPnl": "0",
"fundingFee": "-0.05",
"initialMargin": "300",
"maintenanceMargin": "75",
"leverage": 10,
"marginMode": "CROSSED",
"positionSide": "LONG",
"status": "OPEN",
"closeReason": null,
"closedAt": null,
"updatedAt": "2026-04-26T..."
}
]
}Notes
quantity > 0→LONG;quantity < 0→SHORT.- Returns all positions on your account;
statusis a filter. Other users' positions are never visible (audit fix C-5). - The legacy
?accountId=Xquery parameter is ignored — kept disabled for security.
Example
TS=$(($(date +%s) * 1000))
QUERY="status=OPEN×tamp=${TS}" # alphabetical
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/positions?${QUERY}&signature=${SIG}"const positions = await signedRequest('GET', '/v2/positions', {
params: { status: 'OPEN' },
});positions = signed_request("GET", "/v2/positions", params={"status": "OPEN"}).json()body, _ := zdex.SignedRequest("GET", "/v2/positions",
map[string]string{"status": "OPEN"}, nil)let positions = signed_request(
reqwest::Method::GET,
"/v2/positions",
&[("status", "OPEN")],
None,
).await?;$positions = signed_request('GET', '/v2/positions', ['status' => 'OPEN']);GET /v2/risk/exposure
Gross exposure across open positions.
| Auth | API_KEY |
Response
{
"ok": true,
"grossExposure": "15000.50",
"positions": [/* same Position[] format */]
}grossExposure = sum(|qty|) over open positions. PnL is computed with the same formatPositions logic.
Example
TS=$(($(date +%s) * 1000)); QUERY="timestamp=${TS}"
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/risk/exposure?${QUERY}&signature=${SIG}"const exposure = await signedRequest('GET', '/v2/risk/exposure');exposure = signed_request("GET", "/v2/risk/exposure").json()body, _ := zdex.SignedRequest("GET", "/v2/risk/exposure", nil, nil)let exposure = signed_request(reqwest::Method::GET, "/v2/risk/exposure", &[], None).await?;$exposure = signed_request('GET', '/v2/risk/exposure');GET /v2/me/transfers
Deposit / withdraw history.
| Auth | API_KEY |
Query
skip(optional, int) — pagination offset.take(optional, int, max 100) — page size.type(optional) —"deposit"|"withdraw"|"all"(default).
Response
{
"ok": true,
"transfers": [
{
"id": "...",
"asset": "USDT",
"amount": "100",
"transferType": 1,
"status": "SUCCESS",
"venueTransferId": "0xabc...",
"createdAt": "...",
"updatedAt": "..."
}
],
"total": 42
}transferType: 1 = deposit, 2 = withdraw.
status is one of PENDING | PROCESSING | SUBMITTED | SUCCESS | CONFIRMED | FAILED | CANCELLED.
SUCCESS and CONFIRMED are both terminal-success states (the venue uses
both interchangeably across deposit / withdraw paths). Treat FAILED and
CANCELLED as terminal failures; everything else is in-flight.
Example
TS=$(($(date +%s) * 1000))
QUERY="take=20×tamp=${TS}&type=deposit" # alphabetical
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/me/transfers?${QUERY}&signature=${SIG}"const transfers = await signedRequest('GET', '/v2/me/transfers', {
params: { type: 'deposit', take: 20 },
});transfers = signed_request("GET", "/v2/me/transfers",
params={"type": "deposit", "take": 20}).json()body, _ := zdex.SignedRequest("GET", "/v2/me/transfers",
map[string]string{"type": "deposit", "take": "20"}, nil)let transfers = signed_request(
reqwest::Method::GET,
"/v2/me/transfers",
&[("type", "deposit"), ("take", "20")],
None,
).await?;$transfers = signed_request('GET', '/v2/me/transfers',
['type' => 'deposit', 'take' => 20]);GET /v2/me/activities
Unified activity feed (orders + executions + transfers).
| Auth | API_KEY |
Query
skip,take— pagination.
Response
{
"ok": true,
"data": [
{ "type": "ORDER", "id": "...", "data": { /* Order */ }, "ts": "2026-04-26T12:34:56.789Z" },
{ "type": "EXECUTION", "id": "...", "data": { /* Execution */ }, "ts": "2026-04-26T12:34:55.000Z" },
{ "type": "TRANSFER", "id": "...", "data": { /* Transfer */ }, "ts": "2026-04-26T12:00:00.000Z" }
],
"total": 355,
"totals": {
"totalBuyVolume": "12000.50",
"totalSellVolume": "9800.25",
"totalBuyCount": 142,
"totalSellCount": 113,
"totalOrderBuyCount": 162,
"totalOrderSellCount": 130,
"totalDeposits": "5000.00",
"totalWithdrawals": "1200.00",
"totalDepositCount": 3,
"totalWithdrawalCount": 1
}
}The feed entries live under data (not activities). ts is an ISO-8601
string (Date serialized), not a millisecond epoch. totals is an
aggregate ledger across the account, not a count of returned entries —
it does not change with skip/take.
Designed for UI feeds. For programmatic access, prefer the dedicated
GET /v2/orders, GET /v2/futures/myTrades, and GET /v2/me/transfers
endpoints separately.
Example
TS=$(($(date +%s) * 1000))
QUERY="take=50×tamp=${TS}"
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/me/activities?${QUERY}&signature=${SIG}"const activities = await signedRequest('GET', '/v2/me/activities', {
params: { take: 50 },
});activities = signed_request("GET", "/v2/me/activities", params={"take": 50}).json()body, _ := zdex.SignedRequest("GET", "/v2/me/activities",
map[string]string{"take": "50"}, nil)let activities = signed_request(
reqwest::Method::GET, "/v2/me/activities", &[("take", "50")], None,
).await?;$activities = signed_request('GET', '/v2/me/activities', ['take' => 50]);GET /v2/account/pnl-history
PnL / volume / trade-count series, bucketed by day, week, or month, with running totals.
| Auth | API_KEY |
Query
period—daily|weekly|monthly(defaultdaily).limit— number of buckets to return, default30.
Response
{
"ok": true,
"history": [
{
"date": "2026-04-26",
"pnl": -1.23,
"volume": 5400.00,
"trades": 18
}
],
"summary": {
"totalPnl": -34.5,
"totalVolume": 98750.0,
"totalTrades": 412
}
}The bucket key is in date:
daily→YYYY-MM-DDweekly→YYYY-MM-DDof the week start (Sunday)monthly→YYYY-MM
pnl, volume and summary.* are returned as JSON numbers (not
strings) — this endpoint pre-dates the string-decimals convention and is
based on a 1000-execution rolling window. For an exact ledger-grade PnL,
prefer GET /v2/me/stats or the rebate-claim endpoints under
/v2/me/rebate/*.
pnl here is the executions-side ledger only — fees subtracted, no
funding. volume is Σ quantity × price over the window's executions.
Example
TS=$(($(date +%s) * 1000))
QUERY="limit=30&period=daily×tamp=${TS}"
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/account/pnl-history?${QUERY}&signature=${SIG}"const pnl = await signedRequest('GET', '/v2/account/pnl-history', {
params: { period: 'daily', limit: 30 },
});pnl = signed_request("GET", "/v2/account/pnl-history",
params={"period": "daily", "limit": 30}).json()body, _ := zdex.SignedRequest("GET", "/v2/account/pnl-history",
map[string]string{"period": "daily", "limit": "30"}, nil)let pnl = signed_request(
reqwest::Method::GET, "/v2/account/pnl-history",
&[("period", "daily"), ("limit", "30")], None,
).await?;$pnl = signed_request('GET', '/v2/account/pnl-history',
['period' => 'daily', 'limit' => 30]);GET /v2/accounts/:id/balances
Generic spot-wallet balance for the account's primary sub. For the
futures wallet use GET /v2/futures/balance;
this endpoint pulls the spot ledger from the venue and forwards the raw
payload through.
| Auth | API_KEY |
Path
:id— youraccountId. Passing any other id returns404.
Response — shape is whatever the upstream getBalance connector returns;
typically:
{
"ok": true,
"balances": [
{ "asset": "USDT", "free": "1500.50", "locked": "0" }
]
}If the upstream payload doesn't carry a balances array, the entire
upstream object is forwarded as balances instead — code defensively for
both shapes.
Example
ACCOUNT_ID="cmnw1234..."
TS=$(($(date +%s) * 1000)); QUERY="timestamp=${TS}"
SIG=$(sign "$SECRET" "$QUERY")
curl -H "X-API-KEY: $API_KEY" "$BASE_URL/v2/accounts/${ACCOUNT_ID}/balances?${QUERY}&signature=${SIG}"const accountId = 'cmnw1234...';
const balances = await signedRequest('GET', `/v2/accounts/${accountId}/balances`);account_id = "cmnw1234..."
balances = signed_request("GET", f"/v2/accounts/{account_id}/balances").json()accountID := "cmnw1234..."
body, _ := zdex.SignedRequest("GET",
fmt.Sprintf("/v2/accounts/%s/balances", accountID), nil, nil)let account_id = "cmnw1234...";
let balances = signed_request(
reqwest::Method::GET,
&format!("/v2/accounts/{account_id}/balances"),
&[], None,
).await?;$accountId = 'cmnw1234...';
$balances = signed_request('GET', "/v2/accounts/{$accountId}/balances");