Skip to main content
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.
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.

The flow

1

Markets

GET /perp/markets lists each venue’s markets with the live mark price, and the open fee where the venue publishes one.
2

Quote (optional)

POST /perp/quote prices an open: size, entry, estimated liquidation, fee, and the funding swap when the user pays in another token.
3

Build

POST /perp/instructions with an op returns the instructions, or the unsigned transaction with serialize: true.
4

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.

Markets

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

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: 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: 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: positions show size and entry, orders lists resting limit, take profit and stop loss orders.
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.

Transactions

Perp transactions are v1, like swaps (see 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. detail.counter in Jupiter responses is a 64-bit integer, beyond JavaScript’s safe range: read it as a string if you need it.
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.