POST /info
All public reads use one endpoint with a top-level type discriminator.
A malformed body, or one whose type is missing or not a string, returns HTTP 400 with error.code: "InvalidInfoRequest". A well-formed body whose type is not a known query returns HTTP 400 with error.code: "UnsupportedInfoType":
{
"error": {
"code": "UnsupportedInfoType",
"message": "unsupported public info type `...`"
}
}assets
curl -sS -X POST "$API_URL/info" \
-H 'content-type: application/json' \
-d '{"type":"assets"}'Response:
See Decimals & Units for raw/display conversion and validation rules.
{
"query_height": 180000,
"app_hash": "0x...",
"assets": [
{
"asset_id": 1,
"symbol": "USDC",
"balance_decimals": 8,
"withdraw_fee": "1",
"withdraw_fee_atoms": "100000000",
"issuer": "0x0000000000000000000000000000000000000000",
"credit_ltv": 100,
"credit_ltv_setting": null
},
{
"asset_id": 3,
"symbol": "ETH",
"balance_decimals": 8,
"withdraw_fee": "0.001",
"withdraw_fee_atoms": "100000",
"issuer": "0x0000000000000000000000000000000000000000",
"credit_ltv": 100,
"credit_ltv_setting": null
}
]
}credit_ltv is the asset's loan-to-value percentage for credit accounts — the haircut applied when it counts toward a credit line. credit_ltv_setting echoes the per-asset override when one is configured, null otherwise.
issuer is the owner account bound to an asset by operator-managed metadata. Assets without an issuer binding surface as the zero address. The issuer is a per-asset binding only and confers no privileges beyond the recorded mapping. The assets response does not include cloid; client operation ids are only observable via txStatusByCloid while a transaction is in the recent query window. withdraw_fee_atoms is canonical asset metadata in asset-local atoms; withdraw_fee is formatted from atoms using balance_decimals. On a withdraw this fee is recorded (in the event and /info withdraws) but is not deducted from the balance.
queryStatus
Returns public query-view metadata and the retained recent-height window. This is the public way to discover the height bounds to pass to windowed reads such as userFills. Node role, writable/readable state, control-plane status, and other operational fields remain internal-only.
When no query view is available yet, all fields are null.
quoteAssets
Returns the current canonical quote-asset allowlist sorted by asset_id. min_quantity is the human-readable minimum order notional in the quote token; min_quantity_atoms is the raw integer atom quantity encoded as a decimal string for JavaScript safety.
accountingWithdrawTokens
Returns accounting withdraw-token rows sorted by (chain_id, asset_id). min_withdraw_atoms is the canonical raw atom amount encoded as a decimal string; min_withdraw_amt is formatted using the asset's balance_decimals.
accountingDepositContracts
Returns the configured deposit source contracts sorted by src_chain_id. No parameters.
multisigPolicy
Returns the dynamic non-admin multisig policy for one scope. Only the ACCOUNTING scope (2) is queryable; any other value returns HTTP 400 InvalidMultisigScope.
When a policy is set:
When none is set, found is false and the body echoes scope / scope_name instead of policy.
markets
Response field markets is sorted by market_id. price_decimals and max_price_sig_figs are market metadata, as is base_quantity_decimals. quote_balance_decimals is derived from the quote asset and included as a convenience field for raw notional conversion. See Decimals & Units.
maker_fee_milli_bps / taker_fee_milli_bps are the market's effective fee rates in milli-basis-points — never null, and the only programmatic source of what you will be charged. Divide by 1000 for basis points: 2000 is 2 bps maker, 10000 is 10 bps taker. fee_setting echoes the per-market override when one is configured, null otherwise.
credit_trading is the per-market gate for credit accounts. A credit account may trade a market when its own status is active and either credit_trading is true or the market appears in that account's credit_trading_whitelisted_market_ids (see spotCreditAccount). credit_trading_setting echoes the per-market override, null otherwise.
l2Book
depth is optional, defaults to 20, and is capped by the server.
query_time_ms is the block time of the view you read; last_update_height / last_update_ts_ms are when this market's book last changed. Together they are the staleness signal: a wide gap between last_update_ts_ms and query_time_ms means the book is quiet, not that your read is stale.
withdraws
Per-user retained withdraw records (3-day window), sorted by (block_height, tx_index, withdraw_nonce). Requires user. Each record adds withdraw_fee_atoms (recorded, not deducted).
vault_address is the vault contract the withdrawal pays out from. It is null on records written before vault payouts were activated.
deposits
Per-user retained deposit records. Requires user; the response key is user.
userBalances
accountStatus
Whether an account exists and its freeze state.
found is true once the account exists (it is created on its first deposit). status is "active" or "frozen", and is null when found is false. account_index is the protocol's internal account index, null before the account exists.
spotCreditAccount
spotCreditAccount and spotCreditPositions read a credit account — a protocol-granted account type distinct from the default spot (balance) account that userBalances reads. If you have not been granted a credit line, these report no position. See Account Types.
status is "active" or "frozen". credit_usd_atoms and the available fields are in usd_atoms (USD_SCALE = 10^8). available_usd_atoms is null when any nonzero position asset lacks a mark at the latest query height; accounts with no exposure can report their credit without marks. last_known_available_usd_atoms always uses the most recently committed marks regardless of staleness and is null only when a position asset has never had a mark. Negative fractional USD-atom position values are rounded down conservatively, matching the execution credit gate.
spotCreditPositions
Stage 6 single-leg semantics: a resting ask only debits the base asset; a resting bid only debits the quote asset. The other leg appears as actual_qty only when a fill produces a real settlement delta.
oracleStatus
When the oracle is unavailable, oracle_status is { "status": "unavailable", "reason_code": "ProviderError" } (or MissingRoute, StalePrice, InvalidPrice).
markPrices
asset_ids is optional; omitting it returns all known marks. The response is sorted by asset_id:
usd_atoms is the positive mark price scaled by USD_SCALE = 10^8. source_ts_ms is the upstream provider's per-feed timestamp (Binance spot: worker wall clock at response-fully-read; OKX: per-ticker ts; Binance futures: per-symbol time from /fapi/v2/ticker/price; Llama: per-coin timestamp (seconds) × 1000; cross routes use min(base, quote) of the inputs).
openOrders
Pass market_id: -1 (as the number -1 or the string "-1") to get the owner's open orders across every market in one call. The response echoes "market_id": -1 and applies the same 500-item cap and truncated flag over the union.
original_qty is the submitted order quantity. For an order that partially fills and then rests on the book, filled_qty is nonzero and remaining_qty is the currently open quantity. owner_index is the protocol's internal account index for the owner; it also appears on the order objects returned by orderStatus.
This listing is deliberately lean. To read back how an order was placed — its tif and order_type — query orderStatus for that single oid.
userAgents
agents holds the active agent (API-wallet) slots for the owner. To sign /trade writes, read the epoch of the slot whose agent matches your API-wallet address and pass it as the envelope agent_epoch. agent is null for an empty slot.
userFills
limit must be positive and is capped at max_limit (500). Two different failure shapes, and the difference matters to your parser:
A
limitthat is missing, non-numeric, or beyondu32is rejected with HTTP 400 andInvalidFillsQuery— there is nofillskey at all.A
limitof0, afrom_heightaboveto_height, or a range wider than the recent window returns HTTP 200 with an in-banderrorobject (InvalidFillsQueryorHistoryWindowExceeded) and an emptyfillsarray. On success the response echoes the effectivelimit, yourrequested_limit, andmax_limit.
time is the fill's block timestamp in milliseconds. side is the querying owner's own direction (B bought, A sold), and is null once the taker order has aged out of the window.
tid is the unique id of the trade. The WebSocket trades and userFills frames report the same value, so a backfill merges into a live stream on it. Parse it as a BigInt, not a JS Number.
Each fill reports only the querying owner's side fee (chosen by role). fee_asset_id is the asset the fee was charged in (the asset that side received: base for a bid, quote for an ask), fee is the decimal amount formatted with that asset's balance_decimals, and fee_mode is "balance" or "credit". (Internally — in canonical events and the query model — the fee amount is a raw atom; only this public JSON fee is the formatted decimal string.) Trading fees activate at a network-specific height defined by the code protocol-rules schedule. There are two distinct null cases:
Pre-activation / no-fee fill (below the network's fee activation height):
"fee_asset_id": null, "fee": "0", "fee_mode": null.Fee-active fill whose fee floored to zero: non-null
fee_asset_idandfee_modewith"fee": "0"— so clients can tell the fee regime is live.
The response shape is unchanged, but the fee amount may reflect code-defined per-market and per-account fee overrides (the effective rate is the minimum of the global rate and any matching market/account override). A fully waived market appears as a fee-active fill with "fee": "0" (the second null case above).
userFillsByTime
Per-user fills over a wall-clock window, sorted oldest first. Requires user and start_time_ms; end_time_ms is inclusive like start_time_ms and defaults to the newest indexed block. At most 1000 fills per response, and only the 10000 most recent fills are available.
To page, pass the last fill's time as the next start_time_ms. The bound is inclusive, so that fill repeats — deduplicate on tid.
orderStatus
Query by server order id:
Query by client order id:
orderStatus checks the current open-order view, then falls back to the latest retained status record for that oid or (user, market_id, cloid). Status records are action-result records, not a complete lifecycle stream: a successful incoming order writes one record even when it rests on the book, but later passive maker fills are exposed through userFills and open-order state changes rather than a new status record for the maker order. Explicit cancel and modify actions do write status records for the affected resting order. Outcome-stage failed order attempts also write retained status records when execution has enough order context: IocCancel, FokCancel, BadAloPx, MarketOrderNoLiquidity, and InsufficientSpotCredit can be queried by oid or (user, market_id, cloid) inside the recent query window.
Order-validation failures write no status record at all. Tick size, minimum notional, and insufficient balance are checked before the order is assigned an order id, so there is nothing to look up: they are reported only on the synchronous POST /trade response. This is the same found: false you get for a tx whose block is not published yet, so orderStatus alone cannot tell a refused order from one still in flight — read the /trade response.
A partially filled order that is still on the book reports status: "partiallyfilledresting", not "open" — match on both if you are testing for "still working".
For an in-flight order — a /trade you just sent — you do not poll orderStatus to learn the outcome: the synchronous /trade response already carries it in its response envelope. A just-submitted tx that the query view has not yet advanced to reads back found: false here until its block is published.
Open order response:
Terminal order response:
tif and order_type echo the order action the order was submitted with, in the same spelling you sent: tif is "gtc", "ioc", "fok", or "alo", and order_type is "limit" or "market". They are most useful on the failure statuses — ioccancel, fokcancel, and badalopx are each explained by the tif the order carried:
The openOrders listing does not carry either field — it is the bulk view, and a caller listing hundreds of its own resting orders already knows how it placed them.
Not found:
A query that carries neither a parseable oid nor a complete user + market_id + cloid triple is rejected with HTTP 400 and InvalidOrderStatusQuery. A malformed market_id or cloid is rejected the same way, as InvalidMarketId / InvalidCloid.
txStatusByCloid
Transaction status by cloid uses the transaction authority as the user namespace and does not require a market id. For withdraw, settle, and repay, user is the recovered signer authority.
Failed retained tx responses use the lower-case committed execution error code as status, matching the orderStatus style where terminal failures are reported through the status string:
If a failed retained tx has no single committed error code in its payload, status remains "failed".
The public settle/repay actions also surface here, with action_type "settle" / "repay". Their user namespace is the recovered signer (settle → margin owner; repay → cash owner), so a counterparty named in the action body cannot find the tx. This window is the only cloid-keyed lookup for settle/repay: there is no idempotency, only recent-window visibility bounded by max_recent_txs. The same cloid resubmitted under a new envelope nonce is a separate tx; a lookup returns the latest retained one, and once a tx ages out of the window the response is found: false. A failed settle/repay is retained the same way and reports status "invalidsettle" / "invalidrepay" (or another lower-case committed error code).
Last updated