> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raze.bot/llms.txt
> Use this file to discover all available pages before exploring further.

# Perps

> Open, close and manage perp positions on Jupiter Perps and GMTrade, one transaction per operation.

The router builds perp operations on two venues, **Jupiter Perps** and **GMTrade** (GMX on Solana), with one request shape for both. As for swaps, it never signs and never sends.

<Note>
  Perps are off by default, and they are off on `https://router.raze.bot`. Enable them on your own router with `ROUTER_PERP=all` (or `jupiter`, `gmtrade`). A disabled venue answers `422 venue_disabled`, and `GET /perp/markets` lists no venue.
</Note>

## The flow

<Steps>
  <Step title="Markets">
    `GET /perp/markets` lists each venue's markets with the live mark price, and the open fee where the venue publishes one.
  </Step>

  <Step title="Quote (optional)">
    `POST /perp/quote` prices an open: size, entry, estimated liquidation, fee, and the funding swap when the user pays in another token.
  </Step>

  <Step title="Build">
    `POST /perp/instructions` with an `op` returns the instructions, or the unsigned transaction with `serialize: true`.
  </Step>

  <Step title="Sign, send, watch">
    The transaction creates a request or an order; a venue keeper executes it seconds later in its own transaction. Watch `GET /perp/account` to see the position change.
  </Step>
</Steps>

## Markets

| Venue | Markets | Collateral | Mark |
| - | - | - | - |
| Jupiter Perps (`jupiter`) | `SOL-PERP`, `ETH-PERP`, `BTC-PERP` | A long in the asset itself, a short in USDC | The venue's oracle; refused if older than 90 s |
| GMTrade (`gmtrade`) | Over 90: crypto, US stocks and ETFs, FX, metals, oil | The pool's long token for a long, its short token for a short | Chainlink; refused past the token's heartbeat (30–300 s) |

A market id is `<venue>:<SYMBOL>-PERP`, case-insensitive (`jupiter:SOL-PERP`, `gmtrade:ZEC-PERP`). A GMTrade index with more than one pool spells the pool: `gmtrade:SOL-WSOL-USDC-PERP`. `jup:` and `gm:` are accepted as venue aliases.

<Warning>
  GMTrade quotes and builds need a fresh price: when the feed is older than its heartbeat (outside market hours for stocks and FX, or whenever the feed lags), the answer is `503 oracle`. When the collateral is not USDC, the collateral's own price must be fresh too.
</Warning>

## Units

| Value | Unit |
| - | - |
| USD amounts (`collateralUsd`, `amountUsd`, every `…UsdE6`) | Micro-USD: `10000000` is \$10 |
| Prices (`triggerPriceE6`, `entryPriceE6`, `priceE6`) | USD per whole token × 10⁶ |
| `leverageBps` | Basis points: `10000` is 1x, `50000` is 5x |
| `closeBps` | Share of the position to close: `10000` (default) is all |
| `slippageBps` | Default 50, below 10000 |
| Token amounts (`collateralAmount`, `funding.*`) | The mint's base units |

Notional is collateral × leverage. The router enforces leverage of at least 1x and **no maximum**: an over-levered order is built, and the venue's keeper refuses it at execution. `estLiquidationE6` is an estimate with a 0.6% maintenance margin, not the venue's rule.

## Operations

`POST /perp/instructions` takes `op`, `wallet`, `marketId`, `side` (`long` or `short`) and `clientOrderId` on every operation, plus the fields of the operation:

| `op` | Fields | Jupiter | GMTrade |
| - | - | - | - |
| `open` | `collateralUsd`, `leverageBps`, `slippageBps`, `fundingMint` | Market increase | Market increase; creates the user's venue account in the same transaction if missing |
| `close` | `closeBps`, `slippageBps` | Paid out in **USDC**, longs included | Paid out in the position's collateral |
| `tpsl` | `trigger` (`tp` or `sl`), `triggerPriceE6`, `closeBps` | Trigger request, paid out in USDC | Take profit or stop loss order |
| `limit` | `collateralUsd`, `leverageBps`, `triggerPriceE6` | Not available (`422 venue_form`) | Limit increase: a long below the mark, a short above |
| `update` | `orderId`, `triggerPriceE6` and/or `amountUsd` | Not available: `cancel`, then `tpsl` | New trigger or size for a resting order |
| `cancel` | `orderId` | Collateral back; only 45 s after the request was created | Escrow and execution fee back |
| `deposit` | `amountUsd`, `fundingMint` | Adds collateral to a live position | Adds collateral to a live position |
| `withdraw` | `amountUsd` | Must stay below the position's collateral; paid out in USDC | Must stay below the position's collateral |

`open` on a market and side where the wallet already has a position adds to it (on GMTrade, when that position uses the collateral the router picks; one held in the pool's other token stays separate). A partial reduce is `close` with `closeBps`. The trigger of a take profit on a long, or a stop loss on a short, must be above the mark; the other two below.

## Paying with any token

`fundingMint` chooses what the user pays with on `open`, `limit` and `deposit`:

| `fundingMint` | `fundingMode` | What the transaction does |
| - | - | - |
| Absent, or the collateral mint | `held` | Spends the collateral the wallet already holds |
| `SOL` on a wrapped-SOL collateral | `native` | Wraps SOL, uses it, closes the wrapped account |
| Any other mint | `routed` | A swap into the collateral, through your `swap` deploy, in the same transaction |

Every operation is **one** transaction: the funding swap, the deposit and the order land together or not at all (`atomic` in the response says which parts it contains). A routed operation depends on your `swap` deploy like a swap does: paused, it answers `503`.

## Execution is asynchronous

Neither venue fills inside the user's transaction. The transaction queues a request (Jupiter) or an order (GMTrade), and the venue's keeper executes it in its own transaction, at the price of that moment. A landed transaction means "queued", not "filled". Watch [`GET /perp/account`](/api-reference/perp/account): positions show size and entry, `orders` lists resting limit, take profit and stop loss orders.

<Warning>
  `clientOrderId` (1–64 bytes) names the venue account of the request, but it is **not an idempotency key**: once the first request is executed and closed, the same id acts again. Before retrying, check whether the first transaction landed, or use a new id.
</Warning>

## Transactions

Perp transactions are v1, like swaps (see [Transactions](/router/transactions)): `serialize: true` returns `transaction` (base64, unsigned) and `lastValidBlockHeight`. If you assemble the transaction yourself from `instructions`, set the header from `computeUnits` and `loadedAccountsDataSize`, or post them to [`POST /tx/v1`](/api-reference/router/tx-v1).

`detail.counter` in Jupiter responses is a 64-bit integer, beyond JavaScript's safe range: read it as a string if you need it.

<Tip>
  Before going live on a venue, open and cancel a small position on mainnet with your own deploy: the router builds requests the venue programs accept, but execution is the keeper's.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.