Get Started
An eight-step walkthrough from a cold start to an open position on the Nexus Exchange. You sign in with your wallet, mint an HMAC API key, sign requests, fund the account, browse markets, place an order, cancel or amend it, and monitor positions.
Every step is shown in five clients: cURL, Rust, Python, TypeScript, and the Exchange CLI. Pick a tab per step.
Before you start
You need an Ethereum wallet to sign the login message. Nothing else is required to begin. No wallet yet? Get a wallet and a signature walks through it from a terminal or from MetaMask.
Base URL. Examples below use the public testnet host. Unprefixed paths go under the
/v1transport base,https://api.testnet.nexus.xyz/v1, and/api/v1paths go on the host root, so a full URL readshttps://api.testnet.nexus.xyz/api/v1/…. Substitute the base for the deployment you are calling. Paths are shown with the/api/v1prefix where a versioned route exists. The unprefixed form on the/v1base reaches the same operation and is the one the official clients are moving to. See Base URLs for the other bases the contract declares, and for how theserversoverride on the/api/v1paths resolves.Authentication. Two schemes: a session token (Bearer) used only to create and manage API keys, and HMAC-SHA256 API-key signing used for everything else, including all trading.
Two deployments. The Exchange app is deployed twice from one codebase. The testnet deployment is live and funds accounts from a synthetic-credit faucet. The mainnet real-funds deployment will be funded by bridging USDX from Ethereum Mainnet. It is not live yet: its host,
api.nexus.xyz, does not resolve. Step 4 below differs between the two, and both variants are documented.Placeholders. Values such as
0xSIGNATURE_HEX,nx_7f3a1b...,sess_abc123..., and0x<wallet-private-key>are placeholders. Substitute your own; never commit a secret.
Language coverage
cURL is the baseline, and every step has a cURL example. The four SDK/CLI clients do not yet cover every step. Where a client has no example for a step, the tab says so, and you follow the cURL example instead. The interactive docs did the same, falling back to cURL with a note.
Machine-readable entry points
An agent can start from these without reading this page:
llms.txt, at https://exchange.nexus.xyz/llms.txtopenapi.json, the OpenAPI contract, athttps://api.testnet.nexus.xyz/openapi.json/metadata, the Exchange app's machine-readable metadata routeThe MCP server, published to npm, so adding it is one line. It runs locally over stdio, so your API key stays on your machine:
Its public market-data and demo tools need no credentials, so the command is useful before you have a key. A hosted MCP endpoint is planned; its DNS is not live yet, so there is no remote URL to add today.
The OpenAPI spec and changelog are versioned at nexus-xyz/nexus-exchange-api, and releases are tracked on GitHub Releases.
Authentication
Steps 1–3 get you from a wallet to a signed request.
Get a wallet and a signature
Step 1 needs two things: a wallet, and that wallet's signature over the fixed message Sign in to Nexus Exchange. If you already have both, skip to section 1 (Sign in) below. Otherwise pick one path. Testnet runs on play funds, so a fresh wallet made just for this is the safest choice.
From a terminal, with Foundry's cast
Install Foundry, which ships
cast:Create a wallet.
castprints an address and a private key. Keep the key secret, and use a throwaway key here: theexportlands in your shell history, and--private-keyin the next step is visible to other local users inps.Reusing a wallet that holds real funds? Store it encrypted with
cast wallet import nexus --interactiveand sign with--account nexusinstead of--private-key.Sign the login message.
cast wallet signproduces an EIP-191personal_signsignature by default, which is what the API expects. The message must match exactly, with no trailing newline or extra spaces.Send it to the API and keep the session token (
jqreads the JSON reply):Create an API key with that token. The
secretin the reply is shown once, so save it now.
Using the Exchange CLI instead of curl? Steps 1 and 2 are all you need. With NEXUS_PRIVATE_KEY exported, nexus auth login signs the message for you, and nexus keys create mints the key.
From MetaMask
The quickest path skips curl entirely: open nexus.xyz/api-keys, connect MetaMask, and create a key there. MetaMask signs once, and the page shows the key's private key one time. That page makes an agent key, which signs requests differently from the HMAC key below; see Agents.
To get the login signature yourself and follow steps 1 to 3 with curl:
Install MetaMask and create a wallet, or use one you already have.
Open any web page in the same browser, open the developer console, and run:
Approve the signature request in MetaMask. The console prints a
0x...signature (132 characters).Export it as
SIGNATURE, then run steps 4 and 5 of the terminal path above (login, then create a key).
1. Sign in
POST /auth/login. No authentication required.
Authenticate with an EIP-191 personal signature. The message to sign is always the fixed string "Sign in to Nexus Exchange". The server recovers your wallet address from the signature.
Notes
Session token expires in 24 hours.
You only need the session to create and manage API keys, not to trade.
Not available in TypeScript yet. Use the cURL example.
Request body
Response
2. Create API key
POST /keys. Session token required.
Use the session token to create an HMAC key pair. The secret is shown once, so save it immediately.
Notes
You cannot retrieve the secret after this response.
Keys inherit your account's tier. The
X-RateLimit-*response headers report the per-tier rate limits.
Not available in Python yet. Use the cURL example.
Not available in TypeScript yet. Use the cURL example.
Request body
Response
3. Sign requests
GET /markets. HMAC API key required.
Every authenticated request needs three headers. Build a canonical string, HMAC-SHA256 it with your secret, and attach the result.
Required headers
X-API-Key
yes
Your key ID (nx_...)
X-Timestamp
yes
Current time in ms since epoch
X-Signature
yes
HMAC-SHA256 hex of the canonical string
Notes
Canonical format:
timestamp\nMETHOD\npath\nquery\nsha256(body)pathis the path as written in the contract. Include the/api/v1prefix when the route has one, but not the/v1transport prefix, which is stripped before your signature is verified. Calling…/v1/marketsmeans signing/markets; calling…/api/v1/tickersmeans signing/api/v1/tickers.Timestamp must be within ±30 seconds of server time (milliseconds since epoch).
For GET requests with no body, hash the empty string.
Response
Trading
Steps 4–8 fund the account, put a position on, and monitor it.
4. Fund account
POST /account/credit. HMAC API key required. Testnet only.
Credit synthetic USDX to start trading. Each API key can claim up to 500 USDX per day. Omit "amount" to claim the full remaining daily allowance.
The credit faucet exists only on testnet. On the mainnet real-funds deployment this endpoint returns 403 and funding is bridge-only. See the mainnet variant below.
Notes
Amounts are decimal strings, like all monetary values in the API.
Returns 429 once the daily allowance is used up. The allowance resets the next UTC day.
Request body
Response
Step 4 on mainnet: fund with real collateral
On the mainnet real-funds deployment (planned, not live yet), a real deposit replaces step 4. The testnet play deployment has a synthetic-credit faucet, and the mainnet real-funds deployment has none. You do not have to branch on that yourself. One endpoint answers "how does this deployment accept collateral", and the answer carries the mode.
GET /account/deposit-target. HMAC API key required.
There is no synthetic credit on mainnet. Ask the venue where to send collateral, send it, then poll until your balance reflects it.
Do not hardcode the funding method. The response is a discriminated union on mode. The mode you get is a property of the deployment, not of your request, and no parameter selects one. A deployment with a real deposit contract configured answers onchain. Every other deployment answers testnet-faucet and points at its synthetic-credit endpoints. Branch on mode, and the same client code funds itself on either deployment.
Notes
Discovery only.
GET /account/deposit-targetmoves no funds and creates no deposit. Acting on the returned instruction is a separate, explicit step.In
onchainmode,onchain.addressis the deposit-contract address andonchain.chainis the chain it lives on. Both are deployment configuration, so read them and do not hardcode them.403 EARLY_ACCESS_REQUIREDmeans your account cannot be funded here yet. Where a deployment restricts funding to early-access participants, it refuses an account that is not enrolled, and the refusal is permanent until the account is enrolled. Do not retry. The gate mirrorsPOST /account/creditandPOST /fauceton purpose, so an account that cannot be funded is not told how to fund.The endpoint never fabricates an address to fill the
onchainshape. A deployment configured for on-chain deposits whose address is malformed returns503 DEPOSIT_TARGET_MISCONFIGUREDrather than publishing it, because depositing to a bad address burns the funds. That is an operator misconfiguration, not a transient fault, and retrying does not clear it.min_amountis advisory, not enforced. It is the floor that makes a first trade viable; nothing rejects a smaller on-chain deposit.confirmis identical in both modes, so it works on either deployment. PollGET /accountuntilbalancereflects the funds before trading.POST /account/creditis a testnet-only faucet, and the credited USDX is synthetic. The contract's own guidance is not to build a funding flow that assumes the operation exists on every network. Mainnet has no synthetic credit at all.GET /api/v1/bridge/assetslists the supported chains and, per chain, the depositable assets with their decimals, minimum amount and required confirmations.Track a cross-chain deposit with
GET /api/v1/bridge/deposits, or a single one by its{tx_hash}:{log_index}id viaGET /api/v1/bridge/deposits/{id}. The watcher writes that read model, and the endpoints only read it.On-chain transfers are irreversible. Send only the asset the instruction names, from a wallet you control.
The round trip is: fund here, trade (next steps), then withdraw.
GET /withdrawalslists your records andPOST /withdrawalsinitiates one. It is authenticated by an EIP-712WithdrawIntentsigned with the wallet's own key, not by an API key, so it does not reuse the HMAC credentials above. A bridge withdrawal is a separate operation,POST /api/v1/bridge/withdrawals.
Not available in Rust yet. Use the cURL example. The high-level client wrapper for the bridge step is still in progress.
Not available in Python yet. Use the cURL example.
Not available in TypeScript yet. Use the cURL example.
Not available in the CLI yet. Use the cURL example.
Request body
None. GET /account/deposit-target takes no parameters and no body.
Response (onchain mode, the contract's example for DepositTarget)
5. Browse markets
GET /markets/{market_id}/ticker. HMAC API key required.
List available perpetual futures markets and check current prices.
Response
Price and size precision
Round before you submit. Every market declares three quantisation rules, and the venue rejects an order that misses them rather than rounding it for you.
Field on GET /markets
Rule
tick_size
The limit price must be an exact multiple of it.
lot_size
The order size must be an exact multiple of it.
min_order_size
The order size must be at least this.
For BTC-USDX-PERP those are 0.5, 0.001 and 0.001. So a price of 83000.25 is invalid, because it is not a multiple of 0.5, and so is a size of 0.0005, which is below both the lot and the minimum. 83000.00 and 0.001 are valid.
Read the values per market rather than hardcoding them. They differ by market (ETH-USDX-PERP is 0.10 and 0.01, SOL-USDX-PERP is 0.01 and 0.1), and they are listing-time configuration. Market Specifications publishes the current table for every listed market.
Round toward the safe side. Round a buy price down and a sell price up to the tick, and floor sizes to the lot grid. Rounding a size up can push the order past the margin your equity supports, and the order is then rejected for a different reason.
6. Place an order
POST /orders. HMAC API key required.
Submit a limit or market order. The response confirms acceptance.
Notes
Use
"type": "market"to fill immediately at the best available price.Batch multiple orders in a single
POST /orders/batchcall. The venue processes them in sequence, and the results keep the request order.
Request body
Response
7. Cancel or amend
DELETE /orders/{order_id} · DELETE /orders · PATCH /orders/{order_id}. HMAC API key required.
These are three separate operations:
DELETE /orders/{order_id}
Cancels one resting order.
DELETE /orders
Cancels every resting order, or every order on one market with ?market_id=.
PATCH /orders/{order_id}
Atomic cancel-replace: changes price and/or size in one call.
Notes
An amend returns a replacement with a fresh id. The
200body is the new order; the id you amended no longer exists. Track the id you get back, not the one you sent.At least one of
priceorsizemust be supplied on an amend.The amend's pre-trade margin check excludes the reservation still held by the order being replaced, so it is sized on the margin the replacement adds. Repricing at the same size needs no additional margin, and shrinking frees margin rather than requiring more.
Liquidation orders are not amendable.
A
409on an amend means the order changed after you read it. It filled or was cancelled between your read and your write.Cancels draw on a separate rate-limit budget from submissions. Exhausting your order budget never blocks a cancel. A
429withbucket: cancelis the only refusal that means your cancel channel itself is saturated. See Rate limits.
8. Monitor positions
GET /positions. HMAC API key required.
Check open positions, unrealized PnL, and account health.
Response
WebSockets
Polling is fine to get started. A live book or a fill feed should use the stream.
There are two sockets, and they are not interchangeable:
GET /streamis public market data and needs no token. Send one{"subscribe": [...]}message. Book frames are full top-20 snapshots, so there is nothing to replay after a reconnect.GET /wsneeds a token fromPOST /ws/tokenand carries the per-account channels as well as public ones, withopenvelopes, sequence numbers and a reconnect cursor.
Each page has the channel list, message shapes and reconnection rules.
Before you build against it:
Cancel-on-disconnect is opt-in, per account, and off by default. Set it with
PUT /account/cancel-on-disconnectand read it with theGET. It is a dead-man's switch. When your last authenticated connection drops and does not reconnect inside the grace window, the venue cancels your resting orders. Without it, a dropped connection leaves your orders working.Check
active, not justenabled.enabledis your own opt-in.activealso requires the exchange-side feature switch, soactivetells you whether cancel-on-disconnect will fire. An account can read backenabled: trueand still not be protected.The stream is not the authority for position state. Reconcile against
GET /positionsandGET /accountafter any reconnect rather than replaying from where you left off.
Clients
The snippets above use these packages:
Rust
nexus_exchange
Python
nexus_exchange
TypeScript
@nexus-xyz/exchange-ts
CLI
the nexus command
The CLI and the language SDKs are distributed outside the Exchange monorepo. The guided walkthrough does not state their repositories, so install instructions are not reproduced here.
Next steps
Overview: what the Exchange API covers and how it is organised
Authentication: session tokens, HMAC canonicalisation, and API-key management in full
Order Types: the requirement matrix for all eight order types, and the Trading pages in the Reference sidebar
GET /stream(public, no token) andGET /ws(token fromPOST /ws/token): channels, message shapes, and reconnection
Last updated

