> 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/guides/get-started.md).

# 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](#get-a-wallet-and-a-signature) walks through it from a terminal or from MetaMask.
* **Base URL.** Examples use the public testnet base, `https://api.testnet.nexus.xyz/v1`. See [Base URLs](/api-reference/readme.md#base-urls) for the other bases the contract declares.
* **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...`, and `0x<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.txt>
* `openapi.json`, the OpenAPI contract, at `https://api.testnet.nexus.xyz/openapi.json`
* `/metadata`, the Exchange app's machine-readable metadata route
* The **MCP server**, published to npm, so adding it is one line. It runs locally over stdio, so your API key stays on your machine:

```bash
claude mcp add nexus-exchange -- npx -y @nexus-xyz/exchange-mcp
```

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](https://github.com/nexus-xyz/nexus-exchange-api), and releases are tracked on [GitHub Releases](https://github.com/nexus-xyz/nexus-exchange-api/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`

1. Install Foundry, which ships `cast`:

   ```bash
   curl -L https://foundry.paradigm.xyz | bash
   foundryup
   ```
2. Create a wallet. `cast` prints an address and a private key. Keep the key secret, and use a throwaway key here: the `export` lands in your shell history, and `--private-key` in the next step is visible to other local users in `ps`.

   ```bash
   cast wallet new
   export NEXUS_PRIVATE_KEY=0x<private-key-from-the-output>
   ```

   Reusing a wallet that holds real funds? Store it encrypted with `cast wallet import nexus --interactive` and sign with `--account nexus` instead of `--private-key`.
3. Sign the login message. `cast wallet sign` produces an EIP-191 `personal_sign` signature by default, which is what the API expects. The message must match exactly, with no trailing newline or extra spaces.

   ```bash
   SIGNATURE=$(cast wallet sign --private-key "$NEXUS_PRIVATE_KEY" "Sign in to Nexus Exchange")
   ```
4. Send it to the API and keep the session token (`jq` reads the JSON reply):

   ```bash
   TOKEN=$(curl -s -X POST 'https://api.testnet.nexus.xyz/v1/auth/login' \
     -H 'Content-Type: application/json' \
     -d "{\"message\": \"Sign in to Nexus Exchange\", \"signature\": \"$SIGNATURE\"}" \
     | jq -r .token)
   ```
5. Create an API key with that token. The `secret` in the reply is shown once, so save it now.

   ```bash
   curl -X POST 'https://api.testnet.nexus.xyz/v1/keys' \
     -H "Authorization: Bearer $TOKEN" \
     -H 'Content-Type: application/json' \
     -d '{"label": "my first key"}'
   ```

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](https://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](/api-reference/guides/agent-keys.md).

To get the login signature yourself and follow steps 1 to 3 with curl:

1. Install MetaMask and create a wallet, or use one you already have.
2. Open any web page in the same browser, open the developer console, and run:

   ```js
   const [address] = await ethereum.request({ method: 'eth_requestAccounts' });
   await ethereum.request({
     method: 'personal_sign',
     params: ['Sign in to Nexus Exchange', address],
   });
   ```
3. Approve the signature request in MetaMask. The console prints a `0x...` signature (132 characters).
4. 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.

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "message": "Sign in to Nexus Exchange",
    "signature": "0xSIGNATURE_HEX"
  }'
```

{% endtab %}

{% tab title="Rust" %}

```rust
use nexus_exchange::{Client, Config, EthSigner, Network};

// EIP-191 personal_sign of the fixed login message. The signer
// holds your key only to sign — nothing is written to disk.
let signer = EthSigner::from_hex("0x<wallet-private-key>")?;
let client = Client::new(Config::new(Network::Testnet));

let session = client.sign_in(&signer).await?;
println!("signed in as {}", session.address);
// session.token is a SecretString — hand it to
// Config::session_token to authenticate the /keys endpoints.
```

{% endtab %}

{% tab title="Python" %}

```python
from nexus_exchange import Client, EthSigner

signer = EthSigner.from_hex("0x<wallet-private-key>")

with Client() as client:
    session = client.sign_in(signer)  # EIP-191 personal_sign
    print(session.address, session.token)
```

{% endtab %}

{% tab title="TypeScript" %}
Not available in TypeScript yet. Use the cURL example.
{% endtab %}

{% tab title="CLI" %}

```bash
export NEXUS_PRIVATE_KEY=0x<your-evm-key>
nexus auth login   # signs EIP-191, stores the session token (mode 0600)
```

{% endtab %}
{% endtabs %}

**Request body**

```json
{
  "message": "Sign in to Nexus Exchange",
  "signature": "0xSIGNATURE_HEX"
}
```

**Response**

```json
{
  "token": "sess_abc123...",
  "address": "0xYOUR_WALLET_ADDRESS"
}
```

## 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.

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/keys' \
  -H 'Authorization: Bearer sess_abc123...' \
  -H 'Content-Type: application/json' \
  -d '{"label": "my-bot"}'
```

{% endtab %}

{% tab title="Rust" %}

```rust
use nexus_exchange::{Client, Config, Network};

// /keys endpoints authenticate with the session token from step 1.
let client =
    Client::new(Config::new(Network::Testnet).session_token("sess_..."));

let key = client.create_api_key().await?;
// key.secret is returned once — persist it immediately.
println!("key_id: {}", key.key_id);
```

{% endtab %}

{% tab title="Python" %}
Not available in Python yet. Use the cURL example.
{% endtab %}

{% tab title="TypeScript" %}
Not available in TypeScript yet. Use the cURL example.
{% endtab %}

{% tab title="CLI" %}

```bash
nexus keys create   # secret is shown ONCE — store it now
```

{% endtab %}
{% endtabs %}

**Request body**

```json
{
  "label": "my-bot"
}
```

**Response**

```json
{
  "key_id": "nx_7f3a1b...",
  "secret": "e4d2c8f1...long_hex..."
}
```

## 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**

| Header        | Required | Description                             |
| ------------- | -------- | --------------------------------------- |
| `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)`
* `path` is the path **as written in the contract**, without the `/v1` transport prefix, which is stripped before your signature is verified. Calling `…/v1/markets` means signing `/markets`.
* Timestamp must be within ±30 seconds of server time (milliseconds since epoch).
* For GET requests with no body, hash the empty string.

{% tabs %}
{% tab title="cURL" %}

```bash
# Build the HMAC signature
TIMESTAMP=$(python3 -c 'import time; print(int(time.time()*1000))')
BODY_HASH=$(echo -n "" | shasum -a 256 | cut -d' ' -f1)
CANONICAL="$TIMESTAMP\nGET\n/markets\n\n$BODY_HASH"
SIGNATURE=$(echo -ne "$CANONICAL" | \
  openssl dgst -sha256 -mac HMAC -macopt "hexkey:$NEXUS_API_SECRET" -hex | awk '{print $NF}')

curl 'https://api.testnet.nexus.xyz/v1/markets' \
  -H "X-API-Key: nx_7f3a1b..." \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE"
```

{% endtab %}

{% tab title="Rust" %}

```rust
use nexus_exchange::{Client, Config, Network};

// The SDK builds the canonical string and HMAC-signs every request.
let client = Client::new(Config::new(Network::Testnet).api_key(
    std::env::var("NEXUS_API_KEY")?,
    std::env::var("NEXUS_API_SECRET")?,
));

let markets = client.fetch_markets().await?;
println!("{} markets", markets.len());
```

{% endtab %}

{% tab title="Python" %}

```python
import os
from nexus_exchange import Client

# The SDK builds the canonical string and HMAC-signs every request.
client = Client(
    api_key=os.environ["NEXUS_API_KEY"],
    api_secret=os.environ["NEXUS_API_SECRET"],
)

print(len(client.fetch_markets()), "markets")
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import { Client, Network } from "@nexus-xyz/exchange-ts";

// The SDK builds the canonical string and HMAC-signs every request.
const client = new Client({
  network: Network.Testnet,
  apiKey: process.env.NEXUS_API_KEY,
  apiSecret: process.env.NEXUS_API_SECRET,
});

console.log((await client.fetchMarketSummaries()).length, "markets");
```

{% endtab %}

{% tab title="CLI" %}

```bash
nexus setup            # interactive; stores credentials (mode 0600)
# ...or per shell:
export NEXUS_API_KEY=nx_...
export NEXUS_API_SECRET=...
nexus balance          # every request is HMAC-signed for you
```

{% endtab %}
{% endtabs %}

**Response**

```json
[
  { "id": "BTC-USDX-PERP", "base": "BTC", "quote": "USDX", "status": "active" },
  { "id": "ETH-USDX-PERP", "base": "ETH", "quote": "USDX", "status": "active" }
]
```

{% hint style="info" %}
In the interactive docs this step has a live "Try it" button against `GET /markets`.
{% endhint %}

***

## 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.

{% hint style="warning" %}
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.
{% endhint %}

**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.

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/account/credit' \
  -H 'X-API-Key: nx_7f3a1b...' \
  -H 'X-Timestamp: UNIX_MS' \
  -H 'X-Signature: HMAC_HEX' \
  -H 'Content-Type: application/json' \
  -d '{"amount": "500"}'
```

{% endtab %}

{% tab title="Rust" %}

```rust
use nexus_exchange::types::Decimal;

// `client` is the HMAC-credentialed client from step 3.
// Pass None to claim the full remaining daily allowance.
let credit = client
    .claim_credit(Some("500".parse::<Decimal>()?))
    .await?;
println!("credited {} (today: {})", credit.amount, credit.credited_today);
```

{% endtab %}

{% tab title="Python" %}

```python
# `client` is the HMAC-credentialed client from step 3.
credit = client.claim_credit("500")  # omit the amount for the daily max
print(credit.amount, credit.credited_today)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
// `client` is the HMAC-credentialed client from step 3.
const credit = await client.claimCredit({ amount: "500" }); // {} = daily max
console.log(credit.amount, credit.credited_today);
```

{% endtab %}

{% tab title="CLI" %}

```bash
nexus account credit --amount 500   # omit --amount for the daily max
```

{% endtab %}
{% endtabs %}

**Request body**

```json
{
  "amount": "500"
}
```

**Response**

```json
{
  "amount": "500",
  "credited_today": "500",
  "daily_limit": "500"
}
```

### 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`](/api-reference/account/fetch-deposit-address.md). **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-target` moves no funds and creates no deposit. Acting on the returned instruction is a separate, explicit step.
* In `onchain` mode, `onchain.address` is the deposit-contract address and `onchain.chain` is the chain it lives on. Both are deployment configuration, so read them and do not hardcode them.
* **`403 EARLY_ACCESS_REQUIRED` means 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 mirrors `POST /account/credit` and `POST /faucet` on purpose, so an account that cannot be funded is not told how to fund.
* The endpoint never fabricates an address to fill the `onchain` shape. A deployment configured for on-chain deposits whose address is malformed returns `503 DEPOSIT_TARGET_MISCONFIGURED` rather 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_amount` is **advisory, not enforced**. It is the floor that makes a first trade viable; nothing rejects a smaller on-chain deposit.
* `confirm` is identical in both modes, so it works on either deployment. Poll `GET /account` until `balance` reflects the funds before trading.
* `POST /account/credit` is 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 /bridge/assets`](/api-reference/bridge/fetch-bridge-assets.md) lists the supported chains and, per chain, the depositable assets with their decimals, minimum amount and required confirmations.
* Track a cross-chain deposit with [`GET /bridge/deposits`](/api-reference/bridge/fetch-bridge-deposits.md), or a single one by its `{tx_hash}:{log_index}` id via [`GET /bridge/deposits/{id}`](/api-reference/bridge/fetch-bridge-deposit.md). 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 /withdrawals` lists your records and [`POST /withdrawals`](/api-reference/account/withdraw.md) initiates one. It is authenticated by an EIP-712 `WithdrawIntent` signed 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 /bridge/withdrawals`](/api-reference/bridge/create-bridge-withdrawal.md).

{% tabs %}
{% tab title="cURL" %}

```bash
# 1) Ask the venue how this deployment accepts collateral.
#    Branch on .mode — "onchain" or "testnet-faucet". Do not assume either shape.
curl 'https://api.testnet.nexus.xyz/v1/account/deposit-target' \
  -H 'X-API-Key: nx_7f3a1b...' \
  -H 'X-Timestamp: UNIX_MS' \
  -H 'X-Signature: HMAC_HEX'

# onchain mode answers with the deposit contract to send to:
#   { "mode": "onchain", "account": "0x742d...", "asset": "USDX", "min_amount": "10",
#     "onchain": { "chain": "ethereum-mainnet", "asset": "USDX",
#                  "address": "0x1f98...", "min_amount": "10" },
#     "confirm": { "method": "GET", "path": "/account", "poll_field": "balance" } }

# 2) Send the named asset to onchain.address on onchain.chain from your own wallet.

# 3) Confirm with the instruction's own confirm block — poll until balance moves.
curl 'https://api.testnet.nexus.xyz/v1/account' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'

# Optional: the cross-chain deposit record, once the watcher has seen it.
curl 'https://api.testnet.nexus.xyz/v1/bridge/deposits' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
```

{% endtab %}

{% tab title="Rust" %}
Not available in Rust yet. Use the cURL example. The high-level client wrapper for the bridge step is still in progress.
{% endtab %}

{% tab title="Python" %}
Not available in Python yet. Use the cURL example.
{% endtab %}

{% tab title="TypeScript" %}
Not available in TypeScript yet. Use the cURL example.
{% endtab %}

{% tab title="CLI" %}
Not available in the CLI yet. Use the cURL example.
{% endtab %}
{% endtabs %}

**Request body**

None. `GET /account/deposit-target` takes no parameters and no body.

**Response** (`onchain` mode, the contract's example for `DepositTarget`)

```json
{
  "mode": "onchain",
  "account": "0x742d35cc6634c0532925a3b844bc9e7595f0beb0",
  "asset": "USDX",
  "min_amount": "10",
  "onchain": {
    "chain": "ethereum-mainnet",
    "asset": "USDX",
    "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
    "token_address": "0xf92b58d2225a73b45ded3bc2290ac1a2077c1cf2",
    "min_amount": "10",
    "instructions": "Approve USDX spend for the deposit contract, then call depositTo(token, amount, beneficiary) with token set to `onchain.token_address`, amount in USDX base units, and beneficiary set to `account`. Only USDX is accepted; other tokens are rejected on-chain. Funds credit to the exchange account within 30s of on-chain confirmation."
  },
  "confirm": {
    "method": "GET",
    "path": "/account",
    "poll_field": "balance",
    "note": "Poll until balance reflects the deposit (SPEC target: within 30s of on-chain confirmation)."
  }
}
```

## 5. Browse markets

`GET /markets/{market_id}/ticker`. **HMAC API key required.**

List available perpetual futures markets and check current prices.

{% tabs %}
{% tab title="cURL" %}

```bash
# List markets (4 trading on testnet today; 32 configured)
curl 'https://api.testnet.nexus.xyz/v1/markets' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'

# Get a single ticker
curl 'https://api.testnet.nexus.xyz/v1/markets/BTC-USDX-PERP/ticker' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
```

{% endtab %}

{% tab title="Rust" %}

```rust
let markets = client.fetch_markets().await?;
println!("{} markets", markets.len());

let ticker = client.fetch_ticker("BTC-USDX-PERP").await?;
println!(
    "{}: last={:?} mark={:?}",
    ticker.symbol, ticker.last, ticker.mark_price
);
```

{% endtab %}

{% tab title="Python" %}

```python
for market in client.fetch_markets():
    print(market.market_id)

ticker = client.fetch_ticker("BTC-USDX-PERP")
print(ticker.last, ticker.mark_price)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
for (const market of await client.fetchMarketSummaries()) {
  console.log(market.market_id);
}

const ticker = await client.fetchTicker("BTC-USDX-PERP");
console.log(ticker.last, ticker.markPrice);
```

{% endtab %}

{% tab title="CLI" %}

```bash
nexus markets                 # tradable markets and their rules
nexus ticker BTC-USDX-PERP    # ticker for one market
```

{% endtab %}
{% endtabs %}

**Response**

```json
{
  "symbol": "BTC-USDX-PERP",
  "last": 84250.5,
  "bid": 84249.0,
  "ask": 84252.0,
  "volume": 1523.4,
  "change": 2.1
}
```

{% hint style="info" %}
The source comment for this step reads `# List markets (all 32)`. That is the configured-market count, not the live one. The testnet is currently trading **4** markets, so the comment above has been corrected. See [Exchange Testnet](https://docs.nexus.xyz/exchange/exchange-testnet) for the current set.

On the mainnet deployment the market set differs. Mainnet launches with 3 markets (`BTC-USDX-PERP`, `ETH-USDX-PERP`, `SOL-USDX-PERP`), expanding to 32+. The cURL comment there reads `# List markets (3 at internal launch: BTC, ETH, SOL — expanding to 32+)`.

In the interactive docs this step has a live "Try it" button against `GET /markets/BTC-USDX-PERP/ticker`.
{% endhint %}

### 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](https://docs.nexus.xyz/exchange/trading/perpetuals/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/batch` call. The venue processes them in sequence, and the results keep the request order.

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X POST 'https://api.testnet.nexus.xyz/v1/orders' \
  -H 'X-API-Key: nx_7f3a1b...' \
  -H 'X-Timestamp: UNIX_MS' \
  -H 'X-Signature: HMAC_HEX' \
  -H 'Content-Type: application/json' \
  -d '{
    "market_id": "BTC-USDX-PERP",
    "side": "buy",
    "type": "limit",
    "size": 0.01,
    "price": 83000.00
  }'
```

{% endtab %}

{% tab title="Rust" %}

```rust
use nexus_exchange::types::{OrderRequest, Side, TimeInForce};

let order = OrderRequest::limit(
    "BTC-USDX-PERP",
    Side::Buy,
    "83000".parse()?,
    "0.01".parse()?,
    TimeInForce::Gtc,
);
let placed = client.create_order(&order).await?;
println!("placed {} — {}", placed.order.id, placed.order.status);
```

{% endtab %}

{% tab title="Python" %}

```python
from decimal import Decimal
from nexus_exchange import OrderRequest

order = OrderRequest.limit(
    "BTC-USDX-PERP", "Buy", Decimal("83000"), Decimal("0.01")
)
placed = client.create_order(order)
print(placed.order.id, placed.order.status)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const { order } = await client.placeOrder({
  market_id: "BTC-USDX-PERP",
  side: "Buy",
  order_type: "Limit",
  price: "83000",
  quantity: "0.01",
  time_in_force: "GTC",
});
console.log(order.id, order.status);
```

{% endtab %}

{% tab title="CLI" %}

```bash
# Prompts for confirmation; pass --yes to skip
nexus order place --market BTC-USDX-PERP --side buy --type limit \
  --price 83000 --quantity 0.01 --tif GTC
```

{% endtab %}
{% endtabs %}

**Request body**

```json
{
  "market_id": "BTC-USDX-PERP",
  "side": "buy",
  "type": "limit",
  "size": 0.01,
  "price": 83000.0
}
```

**Response**

```json
{
  "id": "ord_f82a...",
  "status": "open",
  "filled": 0.0,
  "market_id": "BTC-USDX-PERP",
  "side": "buy",
  "type": "limit",
  "size": 0.01,
  "price": 83000.0
}
```

## 7. Cancel or amend

`DELETE /orders/{order_id}` · `DELETE /orders` · `PATCH /orders/{order_id}`. **HMAC API key required.**

These are three separate operations:

| Operation                   | Effect                                                                        |
| --------------------------- | ----------------------------------------------------------------------------- |
| `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 `200` body 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 `price` or `size` must 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 `409` on 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 `429` with `bucket: cancel` is the only refusal that means your cancel channel itself is saturated. See [Rate limits](/api-reference/guides/rate-limits.md).

{% tabs %}
{% tab title="cURL" %}

```bash
# Amend a resting order's price — note the response carries a NEW order id
curl -X PATCH 'https://api.testnet.nexus.xyz/v1/orders/ORDER_ID?market_id=BTC-USDX-PERP' \
  -H 'X-API-Key: nx_7f3a1b...' \
  -H 'X-Timestamp: UNIX_MS' \
  -H 'X-Signature: HMAC_HEX' \
  -H 'Content-Type: application/json' \
  -d '{"price": 82500.00}'

# Cancel everything on one market
curl -X DELETE 'https://api.testnet.nexus.xyz/v1/orders?market_id=BTC-USDX-PERP' \
  -H 'X-API-Key: nx_7f3a1b...' -H 'X-Timestamp: UNIX_MS' -H 'X-Signature: HMAC_HEX'
```

{% endtab %}
{% endtabs %}

## 8. Monitor positions

`GET /positions`. **HMAC API key required.**

Check open positions, unrealized PnL, and account health.

{% tabs %}
{% tab title="cURL" %}

```bash
# Open positions
curl 'https://api.testnet.nexus.xyz/v1/positions' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'

# Account summary
curl 'https://api.testnet.nexus.xyz/v1/account' \
  -H 'X-API-Key: ...' -H 'X-Timestamp: ...' -H 'X-Signature: ...'
```

{% endtab %}

{% tab title="Rust" %}

```rust
let account = client.fetch_balance().await?;
println!("equity {}", account.equity);

for p in client.fetch_positions().await? {
    println!(
        "{} {} size {} | uPnL {}",
        p.market_id, p.side, p.size, p.unrealized_pnl
    );
}
```

{% endtab %}

{% tab title="Python" %}

```python
account = client.fetch_balance()
print(account.equity)

for p in client.fetch_positions():
    print(p.market_id, p.side, p.size, p.unrealized_pnl)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
const account = await client.getAccount();
console.log(account.equity);

for (const p of await client.getPositions()) {
  console.log(p.market_id, p.side, p.size, p.unrealized_pnl);
}
```

{% endtab %}

{% tab title="CLI" %}

```bash
nexus positions   # open positions with PnL
nexus balance     # balance, collateral, equity, margin
```

{% endtab %}
{% endtabs %}

**Response**

```json
{
  "balance": 10000.0,
  "equity": 10012.5,
  "margin_used": 83.0,
  "margin_ratio": 0.008,
  "positions": 1
}
```

{% hint style="info" %}
In the interactive docs this step has a live "Try it" button against `GET /account`.
{% endhint %}

***

## 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 /stream`](/api-reference/websocket/connect-stream.md) is 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 /ws`](/api-reference/websocket/connect-web-socket.md) needs a token from [`POST /ws/token`](/api-reference/websocket/create-ws-token.md) and carries the per-account channels as well as public ones, with `op` envelopes, 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-disconnect` and read it with the `GET`. 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 just `enabled`.** `enabled` is your own opt-in. `active` also requires the exchange-side feature switch, so `active` tells you whether cancel-on-disconnect will fire. An account can read back `enabled: true` and still not be protected.
* **The stream is not the authority for position state.** Reconcile against `GET /positions` and `GET /account` after any reconnect rather than replaying from where you left off.

## Clients

The snippets above use these packages:

| Client     | Package / crate          |
| ---------- | ------------------------ |
| 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](/api-reference/readme.md): what the Exchange API covers and how it is organised
* [Authentication](/api-reference/guides/authentication.md): session tokens, HMAC canonicalisation, and API-key management in full
* [Order Types](/api-reference/guides/order-types.md): the requirement matrix for all eight order types, and the Trading pages in the Reference sidebar
* [`GET /stream`](/api-reference/websocket/connect-stream.md) (public, no token) and [`GET /ws`](/api-reference/websocket/connect-web-socket.md) (token from `POST /ws/token`): channels, message shapes, and reconnection


---

# 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/guides/get-started.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.
