Skip to navigation

Account Data

All account reads are open — no signature, no API key. They are keyed by either wallet or account_id, as defined in Account format; each endpoint below says which it takes.

Results are newest-first unless noted. Pagination is not uniform:

Readslimit / offsettotal
open-orders, order-history, trade-history, funding-historyEnforced: limit 1–200, offset ≥ 0yes
position-triggersAccepted, not validated — treat as unpaginatedyes
deposit-history, withdrawal-historyAccepted, unboundedno

Response shapes

The key inside data differs per endpoint:

Endpointdata
/account/{account_id}/balance{ account, free_balance }
/account/{account_id}/positions{ positions: [] }
/account/open-orders, /account/position-triggers{ orders: [], total }
/account/order-history{ orders: [], limit, offset, total }
/account/trade-history{ fills: [], limit, offset, total }
/account/funding-history{ payments: [], limit, offset, total }
/account/{account_id}/deposit-history{ account, deposits: [] }
/account/{account_id}/withdrawal-history{ account, withdrawals: [] }
/auth/trading-keysa bare array

Balance

GET /account/{account_id}/balance

Returns account and free_balance. Funds locked by an in-flight withdrawal are already excluded from free_balance — they reappear only if the withdrawal is refunded.

free_balance excludes posted margin and unrealized PnL. There is no equity endpoint.

Positions

GET /account/{account_id}/positions

Eventually consistent with fills: an order can read FILLED while /positions is still empty. Poll until the position appears rather than reading once.

Returns data.positions[] — every OPEN position, each with:

FieldMeaning
idPosition identifier
marketMarket symbol, e.g. BTC
sizeSigned size: positive is long, negative is short
entry_priceAverage entry price
isolated_marginCollateral currently posted to this position
closeableHow much can be reduced right now. Size a close from this, not from size
statusPosition status
versionIncrements on every change — use it to detect staleness

There is no leverage on a position; leverage belongs to each order.

Open orders and triggers

GET /account/open-orders?wallet=0x…
GET /account/position-triggers?wallet=0x…
EndpointReturnsParams
/open-ordersLive resting orders plus every trigger — armed, or still waiting for its entrywallet (required), market, limit (default 10), offset, from / to as Unix-ms bounds
/position-triggersEvery armed reduce-only TP/SL trigger, not paginated. A TP/SL still waiting for its entry is not here yetwallet (required)

They return the same item shape. The example below is a TRIGGER order — trigger_price, trigger_direction, tpsl, and is_trigger_market appear only on triggers:

{
"order_id": "…",
"market": "BTC",
"side": "sell",
"size": "0.001",
"filled_size": "0",
"limit_price": "60000",
"order_type": "TRIGGER",
"time_in_force": "IOC",
"reduce_only": true,
"trigger_price": "60500",
"trigger_direction": "down",
"tpsl": "sl",
"is_trigger_market": true,
"status": "open",
"submitted_at": 1787067314951,
"status_at": 1787067314999,
"is_modifying": false
}

is_modifying is true while a modify on this order is in flight — re-read rather than modifying again.

time_in_force is GTC, IOC, or ALO — ALO marks a post-only order.

parent_order_id appears only on a TP/SL attached to an entry that has not filled yet (a bracket leg). Such a leg reads open in /open-orders but is not armed and is absent from /position-triggers; once it arms, the field disappears and /position-triggers picks it up.

Status vocabularies

You write uppercase (BUY, SL); list reads return lowercase (sell, sl); GET /orders/{orderId} returns uppercase. The two order routes also report different sets of states:

GET /orders/{orderId}List readsNote
SUBMITTEDopen on a TRIGGERAn armed trigger reads open
PENDING_PARENTopen on a TRIGGER with parent_order_id setA bracket TP/SL waiting for its entry — not armed
ACCEPTEDopen on a LIMITDisambiguate open by order_type
—triggeredList-only: the trigger fired
EXECUTING, PENDING_MODIFY, FAILED—Single-order route only
FILLED, PARTIALLY_FILLED_CANCELLED, CANCELLED, REJECTEDsame, lowercaseTerminal

History

Three history endpoints, all keyed by wallet and all accepting market, limit, and offset.

Order history

GET /account/order-history?wallet=0x…

One entry per status change, so the same order_id appears once per transition — this is a log, not a list of orders. Rows carry avg_fill_price once filled.

status is one of:

open · triggered · filled · partially_filled_cancelled · cancelled · rejected

A bracket TP/SL waiting for its entry (PENDING_PARENT) is logged as open. Rows also carry time_in_force (ALO marks a post-only order) and, on rejected or cancelled rows, the reason — for example post_only_would_take or a bracket_* code.

Trade history

GET /account/trade-history?wallet=0x…

Executed fills, one entry per fill:

FieldMeaning
tidThe venue’s trade id
price / sizeWhat filled, and at what price
feeFee charged on this fill
closed_pnlRealized PnL — present on reduce and close fills only
dir"Open Long", "Close Short", and so on
is_liquidationWhether the fill came from a liquidation
filled_atFill time, Unix ms
order_idThe order this fill belongs to

Funding history

GET /account/funding-history?wallet=0x…

Hourly funding payments, one entry per position per hour:

FieldMeaning
directionLONG or SHORT
sizePosition size the payment was computed on
paymentSigned — negative means you paid, positive means you received
funding_rateThe rate for that hour
epoch_time / settled_atThe funding period, and when it settled

funding_rate is a raw fraction, not a percentage: 0.000120 is 0.0120%.

Adjust isolated margin

POST /positions/margin

Adds or removes isolated margin on the open (account, market) position. Signed by your trading key as:

UpdateIsolatedMargin(bytes32 account, string market, string amount, uint256 nonce)
{
"account_id": "0x…",
"market": "BTC",
"amount": "25",
"nonce": 1787067314956,
"signature": "0x…"
}
  • amount is a signed canonical decimal: positive adds margin from your free balance, negative removes it.
  • nonce is a JSON integer here — the order routes carry it as a string. See the signed actions table.

Getting the outcome

The adjustment applies asynchronously, in two stages.

The submit response is data: { request_id, status, reason? } where status is SUBMITTED or REJECTED. Note that a rejection arrives as a 200, not an HTTP error. Only shape is validated up front (amount must be a string); an unknown market comes back as a reason, not a 400.

Then poll for the outcome:

GET /positions/margin/{requestId}

It returns status APPLIED or REJECTED, and 404 while the request is still pending.

Either stage can carry a reason:

NO_OPEN_POSITION · UNKNOWN_MARKET · INVALID_REQUEST · INSUFFICIENT_BALANCE
EXCEEDS_MAX_REMOVABLE · TRANSFER_DECLINED · DECLINED

Next: Deposits & Withdrawals