> For the complete documentation index, see [llms.txt](https://docs.nexus.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexus.xyz/api-reference/account/fetch-account-state.md).

# GET /account/state

Consolidated account state

Consolidated account state.

`operationId`: `fetchAccountState`

> **Declared server:** `https://api.testnet.nexus.xyz/v1`. See [Networks](/api-reference/guides/networks.md).

> **Not reachable through CCXT.** Nexus account aggregates. `fetchBalance` is the CCXT-shaped view; these are richer and unmapped by design.

**Authentication:** `agentAuth`, `hmacAuth`.

## Consolidated account state

> Full account state in a single call: the portfolio summary aggregates plus all open positions (\`{ summary, positions }\`). Saves clients from pairing \`/account/summary\` with \`/positions\`, matching Hyperliquid \`clearinghouseState\` ergonomics. Both parts are built from one coherent read, so \`summary.open\_positions\_count\` always matches the \`positions\` length, and the embedded \`summary\` is identical to the standalone \`/account/summary\` response. Fails closed with \`502\` when the engine-authoritative margin view is unavailable.

````json
{"openapi":"3.1.0","info":{"title":"Nexus Exchange API","version":"0.9.101"},"tags":[{"name":"Account","description":"Balance and order management"}],"servers":[{"url":"https://api.testnet.nexus.xyz/v1","description":"Public testnet (play funds) — the default server and /v1 transport base for unversioned paths."},{"url":"https://api.nexus.xyz/v1","description":"Mainnet — **REAL FUNDS**. Collateral is USDX bridged from Ethereum Mainnet; orders placed here move real money, and there is no faucet. The durable per-network base. Listed after testnet on purpose, so nothing that reaches for \"the first https server\" lands on the real-funds target. Not resolvable yet: DNS/TLS is a separate infra change."},{"url":"http://localhost:9090","description":"Local development — the indexer served directly. Not a public network; whatever your local instance holds."}],"security":[{"hmacAuth":[]},{"agentAuth":[]}],"components":{"securitySchemes":{"hmacAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"HMAC API key authentication. Send three headers:\n- `X-API-Key`: your key ID (e.g. `nx_a1b2c3...`)\n- `X-Timestamp`: current time in unix milliseconds\n- `X-Signature`: `hex(hmac_sha256(secret, timestamp + \"\\n\" + METHOD + \"\\n\" + path + \"\\n\" + query + \"\\n\" + sha256_hex(body)))`\n\nTimestamp must be within 30 seconds of server time.\n\n**The key is network-scoped; the signature is not.** The canonical string above has no network component, so the same signed request is byte-identical on every host. What stops a testnet key from authenticating against mainnet is that the key does not exist in that host's key store — sign only for the host that minted the key, and never replay a signed payload against another network. Presented to the wrong host, a key is refused with the same opaque `401` as an unknown key and must not be retried there. See “API keys are bound to one network” in the API description."},"agentAuth":{"type":"apiKey","in":"header","name":"x-agent","description":"Agent-key request signing. An agent is a secp256k1 keypair the wallet delegates trading to with `POST /agents/register`; the agent then signs each request itself, so the wallet key is never exposed. Send four headers:\n\n- `x-agent`: the agent's 20-byte address, hex, `0x` prefix optional — derived from its public key exactly like a wallet address (`keccak256(uncompressed_pubkey[1..])[12..]`). It must be registered on this host and not expired.\n- `x-timestamp`: the current time in unix milliseconds, as a base-10 integer.\n- `x-nonce`: an unsigned 64-bit integer, base 10. Required on every request; see “Nonces” for when it is enforced.\n- `x-signature`: `0x` followed by the hex of the 65-byte signature described under “Signature”.\n\n**Canonical string.** Six fields joined by LF (`\\n`), with no trailing newline:\n\n```\n{METHOD}\\n{path}\\n{query}\\n{sha256_hex(body)}\\n{x-timestamp}\\n{x-nonce}\n```\n\n- `METHOD` is upper-cased. The field order differs from `hmacAuth`: the method comes first and the timestamp comes after the body hash, so an HMAC canonical-string builder cannot be reused.\n- `path` is the logical path the server authenticates, with no host and no query string — the same rule as `hmacAuth`; see “Base paths and the signed path” in the API description.\n- `query` is the raw query string exactly as sent, without the leading `?`. With no query it is the empty string, and its line stays in the string as an empty line; it is never dropped.\n- `sha256_hex(body)` is the lower-case hex SHA-256 of the exact body bytes sent. A request with no body hashes the empty byte string (`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`).\n- `x-timestamp` and `x-nonce` are the same decimal strings sent in those headers.\n\n**Digest.** `keccak256(utf8(canonical_string))`: raw Keccak-256 of the canonical string, **with no EIP-191 prefix and no EIP-712 structure**. A wallet library's `signMessage` / `personal_sign` adds the `\\x19Ethereum Signed Message:\\n` prefix, which produces a different digest and a signature that recovers to some other address. Sign the 32-byte hash directly (for example `eth_account`'s `unsafe_sign_hash`, or `@noble/curves` `secp256k1.sign(digest, key)`).\n\n**Signature.** Recoverable secp256k1 ECDSA over the digest, sent as 65 bytes `r (32) ‖ s (32) ‖ v (1)` with `v ∈ {27, 28}`. `s` must be low (at most half the curve order, per EIP-2). A high-S signature is refused even though it recovers the same signer, because accepting it would let one authorization be presented as two. The server recovers the signer from the digest and requires it to equal `x-agent`.\n\n**Freshness.** `x-timestamp` must be within ±30 seconds of the server's clock, on reads and writes alike. That window is the only bound on how long a captured read signature can be replayed.\n\n**Nonces.** The nonce is parsed on every request but enforced only on writes:\n\n- On a mutating method (anything but `GET`, `HEAD`, `OPTIONS` and `TRACE`), `x-nonce` must be strictly greater than the highest nonce this host has already accepted on a write from the same agent. The accepted value is persisted before the response is sent. Each agent address has its own nonce space, separate from the wallet-signed nonces of `POST /agents/register` and `POST /withdrawals`.\n- On a read, the nonce is neither checked nor consumed, so the same signed read verifies more than once inside its ±30 s window.\n\nA counter of `max(last + 1, now_ms)` is a simple nonce source: it is unique per signer and stays increasing across restarts.\n\n**Concurrent writes from one agent can be refused as replays.** The rule is about arrival order, not issue order. When two writes from the same agent are in flight together and the one with the higher nonce arrives first, the other is refused, and the refusal is the same opaque `401` as a bad signature. The fix is still being decided (ENG-17010). Until then, keep one write in flight per agent key, or register a separate agent key for each concurrent writer.\n\n**Failures are opaque.** Any failure returns the same `401` body with no reason (see the `Unauthorized` response): an unknown or expired agent, a stale timestamp, a malformed header, a wrong digest, a high-S signature or a replayed nonce all look alike. This contract does not say which check failed or in what order the checks run, and a client must not infer either. If a signer is rejected, check it against the test vector below before anything else.\n\n**What an agent key cannot do.** Agent keys are trade-only. **An agent key cannot withdraw**, and it cannot move funds off the account by any route:\n\n- `POST /api/v1/bridge/withdrawals` refuses an agent-signed request with `403` (code `AGENT_CANNOT_WITHDRAW`). The engine's withdrawal route is refused the same way.\n- `POST /withdrawals` requires the wallet's own EIP-712 signature in the body, which an agent key cannot produce.\n- The ordinary-transfer operations (`ownerSignature`) refuse any request that carries `x-agent`.\n\nAgent keys also cannot manage agent credentials: `GET /agents`, `PATCH /agents/{address}` and `DELETE /agents/{address}` refuse them with `403` (code `AGENT_KEY_FORBIDDEN`). A client that holds only an agent key manages them with the wallet's own signature instead (`walletSignature`). And they cannot mint the legacy `POST /ws-tokens` token, which is HMAC-only; use `POST /ws/token`. None of these operations lists `agentAuth`. Every operation that does list it accepts it.\n\n**Test vector.** The key below is a public test key: never fund it or register it. The `x-nexus-test-vectors` extension on this scheme carries this vector and two more (an empty-body `GET`, and a `DELETE` with a query). Together they cover both values of `v`.\n\n- private key: `0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318` → `x-agent: 0x2c7536e3605d9c16a7a3d7b1898e529396a65c23`\n- request: `post /orders` (the method is lower-case on purpose; the canonical string upper-cases it), no query, body `{\"market_id\":\"BTC-USDX-PERP\",\"side\":\"Buy\",\"order_type\":\"Limit\",\"quantity\":\"0.1\",\"price\":\"50000\"}`, `x-timestamp: 1776033900123`, `x-nonce: 42`\n- canonical string (LF shown as `⏎`): `POST⏎/orders⏎⏎59ddf9017ca0eb5f8802afd1a2d1584b2d93b0260a0fcd62a1b94b3ff92b332c⏎1776033900123⏎42`\n- digest: `0xe27ade6101c417d2c393abfce8f35b6e1551cf0ce5c50dfbe12f8d6f399ffc35`\n- `x-signature: 0xeef09105834062da10d208be38311995d04cbc5e05043b89e7cbe6d4497d11b51f61729639551a8cfb6ea3e4fe0765afeba7bf3b5f5e09f63bc095c89005b61d1c`\n\n**Network scope.** Like every credential here, an agent registration belongs to the host it was registered on. The canonical string has no network component, so the refusal elsewhere comes from the agent being unknown to that host."}},"schemas":{"AccountState":{"type":"object","description":"Consolidated single-call account snapshot — the portfolio summary aggregates plus all open positions — matching Hyperliquid `clearinghouseState` ergonomics. Both parts are built from one coherent read, so `summary.open_positions_count` always equals the length of `positions`, and the embedded `summary` is identical to the standalone `/account/summary` response.","required":["summary","positions"],"properties":{"summary":{"$ref":"#/components/schemas/AccountPortfolioSummary"},"positions":{"type":"array","description":"All open positions for the account.","items":{"$ref":"#/components/schemas/Position"}}}},"AccountPortfolioSummary":{"type":"object","description":"Portfolio summary for the authenticated account (aggregate equity, PnL, volume, open counts).","properties":{"collateral":{"$ref":"#/components/schemas/Decimal"},"total_equity":{"$ref":"#/components/schemas/Decimal"},"total_unrealized_pnl":{"$ref":"#/components/schemas/Decimal"},"total_realized_pnl_24h":{"allOf":[{"$ref":"#/components/schemas/Decimal"}],"description":"Rolling 24-hour realized PnL for the account, as a decimal string: PnL booked when a position closes, funding excluded. Best-effort — see `total_realized_pnl_24h_estimated`."},"total_realized_pnl_24h_estimated":{"type":"boolean","description":"`true` when `total_realized_pnl_24h` is known to undercount: the source closed-positions buffer was at capacity **and** its oldest retained close still falls inside the 24-hour window, so closes inside that window were evicted. Both conditions are required — a full buffer whose oldest entry is already older than the window has lost nothing inside it, and reports `false`. `false` is **not** a guarantee that the window is complete: a buffer that started empty or partial (a service instance with no history to hydrate from) undercounts too, and from the server's side that is indistinguishable from an account that simply closed few positions. Read the flag as a floor on the doubt, not a completeness certificate."},"total_realized_pnl_30d":{"allOf":[{"$ref":"#/components/schemas/Decimal"}],"description":"Rolling 30-day realized PnL for the account, as a decimal string. Counts exactly what `total_realized_pnl_24h` counts, over a longer window: PnL booked when a position closes. Funding settlements are excluded from both, so neither field matches a lifetime realized-PnL figure that folds funding in. Best-effort — see `total_realized_pnl_30d_estimated`."},"total_realized_pnl_30d_estimated":{"type":"boolean","description":"`true` when `total_realized_pnl_30d` is known to undercount: the source closed-positions buffer was at capacity **and** its oldest retained close still falls inside the 30-day window, so closes inside that window were evicted. Same two-part test as `total_realized_pnl_24h_estimated`, against the longer cutoff — so this field can be `true` while the 24-hour one is `false` (the buffer reaches back past a day but not past a month), and whenever the 24-hour flag is `true` this one is too. `false` is **not** a guarantee that the full 30-day window is covered: a buffer that started empty or partial (a service instance with no history to hydrate from) undercounts too, and from the server's side that is indistinguishable from an account that simply closed few positions. Read the flag as a floor on the doubt, not a completeness certificate."},"total_volume_24h":{"allOf":[{"$ref":"#/components/schemas/Decimal"}],"description":"Traded notional (price × size, summed) over the last 24 hours, as a decimal string. Best-effort — see `total_volume_24h_estimated`."},"total_volume_24h_estimated":{"type":"boolean","description":"`true` when `total_volume_24h` is known to undercount: the source fills buffer was at capacity **and** its oldest retained fill still falls inside the 24-hour window, so fills inside that window were evicted. Both conditions are required — a full buffer holding a fortnight of fills has an intact 24-hour window and reports `false`. `false` means no in-window eviction was detected, not that the window is certified complete."},"open_positions_count":{"type":"integer"},"open_orders_count":{"type":"integer"},"margin_used":{"$ref":"#/components/schemas/Decimal"},"available_margin":{"$ref":"#/components/schemas/Decimal"},"withdrawable":{"allOf":[{"$ref":"#/components/schemas/Decimal"}],"description":"Wallet-withdrawable balance: engine-authoritative free margin floored at zero (`max(0, available_margin)`). Free margin already nets each position's initial margin and pre-trade order reservations out of equity, so this is exactly what can leave the account. A negative free margin (an underwater account) is clamped to `\"0\"` and never surfaced negative. Derived from the authoritative margin view — the endpoint fails closed with `502` rather than reporting a local estimate when that view is unavailable."},"early_access_allowed":{"type":"boolean","description":"Present only when the early-access gate is active."}}},"Decimal":{"type":"string","description":"Arbitrary-precision decimal serialized as a string (lossless). Parse with a decimal type, never a float."},"Position":{"type":"object","description":"An open position with per-position risk detail. Enriched risk fields are derived strictly from indexer-mirrored state (no engine round-trip, to stay on the low-latency read path): when an input is not mirrored, the field is `null` and its companion `<field>_error` carries a machine-readable reason rather than a fabricated number. Monetary fields are lossless decimal strings; leverage fields are JSON numbers.\n\nField names follow CCXT's unified vocabulary wherever CCXT defines one and stay `snake_case` for the Nexus extensions it does not — the same rule `Order` and `Trade` follow (ENG-15117, which renamed thirteen fields and carried eight `<field>_error` twins with them to `<ccxtName>_error`). `size`, `roe`, `max_leverage` and `funding_paid` keep the venue's spelling: CCXT defines no counterpart for a position's base-asset quantity (its `contracts` is `size / contractSize`, a different number), for a bare return ratio (its `percentage` is per hundred), for a market's leverage ceiling, or for a paid-positive cumulative funding figure.\n\n**`GET /account` does not answer this schema.** That route relays the matching engine's own position response — a narrower object, under the engine's names, carrying `funding_accrued`, `fee_pnl` and `entry_pnl` which are absent here. A client reading both surfaces reads two vocabularies. `PositionPnl` (`GET /positions/pnl`) is likewise a separate schema that this rename did not reach. `ClosedPosition` (`GET /positions/closed`) WAS one until ENG-15258, which carried the same five renames onto it — it now serves `symbol`, `entryPrice`, `realizedPnl`, `lastPrice` and `lastUpdateTimestamp`, and the fields it does not serve are the open-only ones a closed position cannot state.","properties":{"symbol":{"type":"string"},"side":{"type":"string","enum":["Long","Short"]},"size":{"$ref":"#/components/schemas/Decimal"},"entryPrice":{"$ref":"#/components/schemas/Decimal"},"markPrice":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"The mark price this position's `unrealizedPnl`, `notional` and `initialMargin` were computed against. This is the real blended mark that margin and liquidations use — not the trade reference that `FundingSample.mark_price` carries under the same name. Mirrored from the engine's `GET /markets/{market_id}/mark-price` by the indexer's per-cycle poll, and treated as unavailable when it is absent OR staler than the freshness bound, so a frozen last-good mark is never served as live (ENG-5909).\n\n`null` when the mark is unavailable — see `markPrice_error`. It used to be served as the string `\"0\"` in that case, a sentinel a client could not tell apart from a genuine zero; the other mark-dependent fields on this object (`notional`, `initialMargin`, `roe`) already reported that absence as `null` + `*_error: \"mark_price_unavailable\"`, and this field now follows the same rule (ENG-13921)."},"markPrice_error":{"type":["string","null"],"description":"Machine-readable reason `markPrice` is `null` (`mark_price_unavailable`), or `null` when populated."},"unrealizedPnl":{"$ref":"#/components/schemas/Decimal"},"realizedPnl":{"$ref":"#/components/schemas/Decimal"},"liquidationPrice":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Price at which the position is liquidated, as a decimal string. `null` when the venue has not computed one — see `liquidationPrice_error`. A zero is a real price and never a stand-in for absence: this field used to carry the string `\"0\"` to mean \"not computed\", which made a position that liquidates only at zero — i.e. effectively cannot be liquidated — indistinguishable from one whose liquidation price is simply unknown."},"liquidationPrice_error":{"type":["string","null"],"description":"Machine-readable reason `liquidationPrice` is `null`. Absent, or `null`, when `liquidationPrice` is populated — the indexer omits the sibling rather than nulling it, and `/account` (relayed from the engine) does not carry it at all. On the indexer-served reads it is always `margin_state_not_mirrored`: liquidation price needs margin-module state the indexer does not mirror."},"leverage":{"type":["number","null"],"description":"Position leverage (the account's leverage multiplier for this position). Currently always `null`: deriving it needs the user's leverage setting or account equity/allocated margin, which the indexer does not mirror; when `null`, `leverage_error` carries the reason. Do not infer leverage from `initialMargin` — that collapses to `1/initial_margin_rate`, a per-market constant, not the real leverage."},"leverage_error":{"type":["string","null"],"description":"Machine-readable reason `leverage` is `null`, or `null` when `leverage` is populated. Currently always `margin_state_not_mirrored`."},"notional":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Position notional value (|size| × mark price), as a decimal string. `null` when the mark price is unavailable — see `notional_error`."},"notional_error":{"type":["string","null"],"description":"Machine-readable reason `notional` is `null` (e.g. `mark_price_unavailable`), or `null` when populated."},"roe":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Return on equity: `unrealizedPnl / initialMargin` (return on initial margin), as a decimal string. `null` when a required input is unavailable or margin is zero — see `roe_error`."},"roe_error":{"type":["string","null"],"description":"Machine-readable reason `roe` is `null` (e.g. `mark_price_unavailable`, `margin_rate_unavailable`, `margin_used_zero`), or `null` when populated."},"initialMargin":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Initial-margin requirement held against this position (`notional × initial_margin_rate`, under the engine's cross-margin model), as a decimal string. Isolated/custom margin allocations are not mirrored by the indexer. `null` when a required input is unavailable — see `initialMargin_error`."},"initialMargin_error":{"type":["string","null"],"description":"Machine-readable reason `initialMargin` is `null` (e.g. `mark_price_unavailable`, `margin_rate_unavailable`), or `null` when populated."},"max_leverage":{"type":["integer","null"],"description":"Maximum leverage allowed for this market (from market risk params), as an integer matching `max_leverage` on `/markets/{market_id}/risk-params`. `null` when market params are unavailable — see `max_leverage_error`."},"max_leverage_error":{"type":["string","null"],"description":"Machine-readable reason `max_leverage` is `null` (e.g. `market_params_unavailable`), or `null` when populated."},"funding_paid":{"allOf":[{"$ref":"#/components/schemas/Decimal"}],"description":"Cumulative funding paid on this position, as a decimal string. Sign is **paid-positive**: a positive value means the position has paid funding, a negative value means it has received funding. Always present: `\"0\"` when no funding has accrued. Bounded by the funding history the indexer retains. \n\n**The sign is not uniform across the API, and this field is the exception rather than the rule.** `paid-positive` is chosen here for parity with Hyperliquid's `cumFunding`. The sibling decomposition served at `GET /positions/pnl` reports the same cash flow as `funding_pnl` with the OPPOSITE sign — received-positive, so that it sums with the other P&L components — and the service negates between the two. A client that reads both and assumes one convention will render one of them backwards, and because both are plausible numbers nothing will look wrong. Negate when moving a value between the two fields."},"contractSize":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Base-asset quantity one contract represents, mirrored from the market's parameters — the same value `GET /markets` publishes. `\"1\"` on every market listed today. `null` while market parameters have not yet loaded — see `contractSize_error`."},"contractSize_error":{"type":["string","null"],"description":"Machine-readable reason `contractSize` is `null` (`market_params_unavailable`), or `null` when populated."},"lastPrice":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"The market's last traded price, as a decimal string. `null` before the market's first fill — see `lastPrice_error`.\n\n**Distinct from `markPrice`,** which is oracle-derived and is what margin and liquidation are computed against. This is the last execution. They differ, and substituting one for the other is wrong in a way that is hard to notice."},"lastPrice_error":{"type":["string","null"],"description":"Machine-readable reason `lastPrice` is `null` (`no_trades_yet`), or `null` when populated."},"timestamp":{"anyOf":[{"$ref":"#/components/schemas/TimestampMs"},{"type":"null"}],"description":"When this position was opened, in epoch milliseconds. `null` when no engine position snapshot has been folded for it yet — see `timestamp_error`.\n\n**Distinct from `lastUpdateTimestamp`:** one is when the position began, the other when it was last written to. A position that has traded since opening has two different values here, and the open instant is the one that orders a position list by age."},"timestamp_error":{"type":["string","null"],"description":"Machine-readable reason `timestamp` is `null` (`position_snapshot_not_seen`), or `null` when populated."},"lastUpdateTimestamp":{"$ref":"#/components/schemas/TimestampMs","description":"When this position was last written to, in epoch milliseconds. Always present — every path that creates or refreshes a position entry stamps it, so there is no absence to report and no companion `_error` field."},"marginMode":{"type":["string","null"],"enum":["cross","isolated",null],"description":"Margin mode for this position. **Per-position, not per-account:** a market whose risk class forces isolated margin holds that mode while the same account's other positions may be cross. `null` when no engine position snapshot has been folded for it yet — see `marginMode_error`."},"marginMode_error":{"type":["string","null"],"description":"Machine-readable reason `marginMode` is `null` (`position_snapshot_not_seen`), or `null` when populated."},"collateral":{"anyOf":[{"$ref":"#/components/schemas/Decimal"},{"type":"null"}],"description":"Margin allocated to this position specifically, as a decimal string. Meaningful only for an isolated position. `null` — with a reason in `collateral_error` — in three cases that are deliberately kept apart: a **cross** position has no per-position allocation at all because the account backs it (`cross_margin_no_allocation`); an **isolated** position may have none posted yet (`isolated_margin_not_allocated`); and a position with no folded snapshot has one we cannot state (`position_snapshot_not_seen`). The first is an answer, not a gap."},"collateral_error":{"type":["string","null"],"description":"Machine-readable reason `collateral` is `null` (`cross_margin_no_allocation`, `isolated_margin_not_allocated`, or `position_snapshot_not_seen`), or `null` when populated."}}},"TimestampMs":{"type":"integer","format":"int64","description":"Unix epoch timestamp in milliseconds."},"RateLimitError":{"type":"object","description":"The body of a 429. Branch on `code` (always `RATE_LIMIT_EXCEEDED`) and on `bucket` to learn WHICH budget bottlenecked. Schematised rather than left to `examples` because `bucket` is meant to be read by machines: an example gives a generated client no field and a validator nothing to check `\"bucket\": \"ownr\"` against. `bucket` here and the `X-RateLimit-Bucket` header carry the same value and the same enum.","required":["code","message","bucket"],"properties":{"code":{"type":"string","enum":["RATE_LIMIT_EXCEEDED"],"description":"Always `RATE_LIMIT_EXCEEDED` on this response. The other 429s this API can return carry different codes (`too_many_agents`, `rate_limited`, `cap_exceeded`, `daily_limit_exceeded`) and are not this shape."},"message":{"type":"string","description":"A diagnostic whose wording is NOT stable. Do not parse it; branch on `bucket`."},"bucket":{"type":"string","enum":["key","owner","order","cancel","ip","login"],"description":"Which budget refused this request. `key` — the per-API-key bucket; `owner` — the per-account request bucket; `order` — the trading-action bucket, order submission and amend only; `cancel` — the separate cancellation bucket, `DELETE` on the order surface only; `ip` — the public per-IP bucket for unauthenticated and gateway traffic; `login` — the dedicated per-IP bucket for `POST /auth/login`, tighter than `ip` and drawn from its own pool, so a login refusal leaves the read budget untouched and vice versa. The buckets are independent, so a caller at its read ceiling can still submit orders, and a caller at its submission ceiling can still cancel — do not back off the whole client on any one of them."},"tier":{"type":"string","description":"The rate-limit tier the refused request was charged against — `public` for the per-IP bucket, otherwise the authenticated key's tier. Absent on responses from paths that refuse before a tier is resolved."}}}},"responses":{"Unauthorized":{"description":"Authentication failed. All 401 responses return the same opaque body to prevent information leakage. A credential minted on a different network is one of the causes it hides — by design, so that a key id cannot be probed for existence on another network. Do not retry it against another host.","content":{"application/json":{}}},"RateLimited":{"description":"Rate limit exceeded. **Retryable** — honour `retry-after` and back off; this is what separates a `429` from the permanent `403` jurisdiction refusals. Branch on the body's `code`, which is always `RATE_LIMIT_EXCEEDED`, and on `bucket` to learn WHICH budget bottlenecked — the buckets are independent, so a caller at its read ceiling can still submit orders and should not back off the whole client. Do not parse `message`: it is a diagnostic whose wording is not stable. Requests are charged by weight, so exhausting a budget takes fewer heavy calls than `x-ratelimit-limit` suggests. See “Rate limits” in the API description.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Budget of request weight per second for your tier — not a count of requests. Sent on successful responses too."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Tokens left, in unit-cost requests: 10 means ten weight-1 requests or two weighing 5. Sent on successful responses too. Always `0` on this response."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp (seconds) when the budget is whole again. Sent only on a `429`, never on a success."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying, never below 1. Derived from the **weighted** cost of the refused request, so it is not in the same unit as `X-RateLimit-Remaining`. Sent only on a `429`."},"X-RateLimit-Bucket":{"schema":{"type":"string","enum":["key","owner","order","cancel","ip","login"]},"description":"Which budget refused this request, mirroring the body's `bucket`. `key` — the per-API-key bucket; `owner` — the per-account request bucket; `order` — the trading-action bucket, order submission and amend only; `cancel` — the separate cancellation bucket, `DELETE` on the order surface only; `ip` — the public per-IP bucket for unauthenticated and gateway traffic; `login` — the dedicated per-IP bucket for `POST /auth/login`, tighter than `ip` and drawn from its own pool, so a login refusal leaves the read budget untouched and vice versa. The read, trading and cancellation budgets are counted separately, so a `429` on one does not mean the others are exhausted — in particular a `429` naming `order` never means your cancels are refused. Sent only on a `429`."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"AuthoritativeMarginUnavailable":{"description":"The engine-authoritative margin view is temporarily unavailable. Endpoints that derive balances from it (e.g. `withdrawable`) fail closed — returning this error rather than a locally-estimated, potentially unsafe figure. Transient; retry after a short delay.","content":{"application/json":{}}}}},"paths":{"/account/state":{"get":{"tags":["Account"],"summary":"Consolidated account state","description":"Full account state in a single call: the portfolio summary aggregates plus all open positions (`{ summary, positions }`). Saves clients from pairing `/account/summary` with `/positions`, matching Hyperliquid `clearinghouseState` ergonomics. Both parts are built from one coherent read, so `summary.open_positions_count` always matches the `positions` length, and the embedded `summary` is identical to the standalone `/account/summary` response. Fails closed with `502` when the engine-authoritative margin view is unavailable.","operationId":"fetchAccountState","responses":{"200":{"description":"Consolidated account state (summary aggregates + open positions).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountState"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/AuthoritativeMarginUnavailable"}}}}}}
````

**Test it** cannot sign this request. `X-Signature` is an HMAC computed per call: see [Authentication](/api-reference/guides/authentication.md), or use an SDK.

***

<sub>API version</sub> <sub></sub><sub>`0.9.131`</sub>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.nexus.xyz/api-reference/account/fetch-account-state.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
