Skip to main content
The /order + /execute flow is the recommended way to swap on Jupiter. You get the best price (all routers compete, including RFQ market makers), and Jupiter handles transaction landing for you.

Quick start

Three steps: get an order, sign it, execute it.

Prerequisites

There are several ways to load a wallet for testing. All examples on this page use BS58_PRIVATE_KEY from your .env file.
// .env: BS58_PRIVATE_KEY=your_base58_secret_key

// @solana/kit
import { createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/kit";
const signer = await createKeyPairSignerFromBytes(
  getBase58Encoder().encode(process.env.BS58_PRIVATE_KEY!),
);

// @solana/web3.js
import { Keypair } from "@solana/web3.js";
import bs58 from "bs58";
const signer = Keypair.fromSecretKey(bs58.decode(process.env.BS58_PRIVATE_KEY!));
Never commit private keys to source control. Use environment variables or the Solana CLI keyfile for testing. In production, use a proper key management solution.
import {
  createKeyPairSignerFromBytes,
  getBase58Encoder,
  getTransactionDecoder,
  getTransactionEncoder,
  partiallySignTransaction,
} from "@solana/kit";

type OrderResponse = {
  transaction: string;       // base64-encoded transaction
  requestId: string;
  outAmount: string;
  router: string;            // "iris" | "jupiterz" | "dflow" | "okx"
  mode: string;              // "ultra" | "manual"
  feeBps: number;
  feeMint: string;
};

type ExecuteResponse = {
  status: "Success" | "Failed";
  signature: string;
  code: number;
  inputAmountResult: string;
  outputAmountResult: string;
  error?: string;
};

const API_KEY = process.env.JUPITER_API_KEY;
if (!API_KEY) throw new Error("Missing JUPITER_API_KEY");
const BASE_URL = "https://api.jup.ag/swap/v2";

// Load wallet from base58 secret key in .env
const signer = await createKeyPairSignerFromBytes(
  getBase58Encoder().encode(process.env.BS58_PRIVATE_KEY!),
);

Code example

// Step 1: Get an order
const orderResponse = await fetch(
  `${BASE_URL}/order?` +
    new URLSearchParams({
      inputMint: "So11111111111111111111111111111111111111112", // SOL
      outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
      amount: "100000000",
      taker: signer.address,
    }),
  { headers: { "x-api-key": API_KEY } },
);
if (!orderResponse.ok) {
  console.error(`/order failed: ${orderResponse.status}`, await orderResponse.text());
  process.exit(1);
}
const order: OrderResponse = await orderResponse.json();

if (!order.transaction) {
  console.error("No transaction in response:", JSON.stringify(order, null, 2));
  process.exit(1);
}

// Step 2: Sign the transaction
// Use partiallySignTransaction because JupiterZ quotes require an additional
// market maker signature, which is added during /execute
const transactionBytes = Buffer.from(order.transaction, "base64");
const transaction = getTransactionDecoder().decode(transactionBytes);
const signedTransaction = await partiallySignTransaction(
  [signer.keyPair],
  transaction,
);

// Step 3: Execute
const signedTxBytes = getTransactionEncoder().encode(signedTransaction);
const signedTxBase64 = Buffer.from(signedTxBytes).toString("base64");

const executeResponse = await fetch(`${BASE_URL}/execute`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": API_KEY,
  },
  body: JSON.stringify({
    signedTransaction: signedTxBase64,
    requestId: order.requestId,
  }),
});
if (!executeResponse.ok) {
  console.error(`/execute failed: ${executeResponse.status}`, await executeResponse.text());
  process.exit(1);
}
const result: ExecuteResponse = await executeResponse.json();

console.log(`https://solscan.io/tx/${result.signature}`);
if (result.status === "Success") {
  console.log("Swap successful:", JSON.stringify(result, null, 2));
} else {
  console.error("Swap failed:", JSON.stringify(result, null, 2));
}

How it works

1. Get an order

GET /order returns a quote and an assembled transaction in a single call. All routers compete for the best price: Metis, JupiterZ, Dflow, and OKX. Required parameters:
ParameterDescription
inputMintMint address of the token you are selling
outputMintMint address of the token you are buying
amountAmount in the smallest unit of the input token
takerYour wallet address. Required to receive an assembled transaction.
Without taker, you get a quote but no transaction. This is useful for price checks.
Key response fields:
FieldDescription
transactionBase64-encoded transaction to sign. Null if taker is not set.
requestIdPass this to /execute.
outAmountExpected output amount before slippage.
routerWhich router won the quote (iris, jupiterz, dflow, okx).
mode”ultra” (no optional params) or “manual” (optional params used).
For the full parameter reference and how each parameter affects routing, see Routing.

2. Sign the transaction

  • The transaction returned by /order is unsigned. Sign it with your wallet’s private key. The example above uses @solana/kit. The transaction is a versioned transaction (v0).
  • Note: we use the partiallySignTransaction for partial signing because when JupiterZ routing is provided, there is an additional signer which is the MM that will be required after sending the transaction to /execute request.

3. Execute the transaction

POST /execute takes the signed transaction and the requestId from the order response. Jupiter handles:
  • Optimised slippage via RTSE (Real-Time Slippage Estimator), which adjusts slippage at execution time to balance trade success and price protection
  • Optimised priority fee strategy for current network conditions
  • Jupiter Beam (our own proprietary transaction execution pipeline) for accelerated transaction sending and landing across multiple RPC providers
  • Confirmation polling
  • Parses both successful and failed transactions
Request body:
FieldRequiredDescription
signedTransactionYesBase64-encoded signed transaction
requestIdYesThe requestId from the /order response
lastValidBlockHeightNoBlock height for nonce validation
Response:
FieldDescription
status”Success” or “Failed”
signatureTransaction signature (present on both success and some failures)
codeError code. 0 = success. See error codes below.
inputAmountResultAmount of input token used for the swap
outputAmountResultAmount of output token received

Error codes

CodeCategoryMeaning
0SuccessTransaction confirmed
-1ExecuteMissing cached order (requestId not found or expired)
-2ExecuteInvalid signed transaction
-3ExecuteInvalid message bytes
-1000AggregatorFailed to land
-1001AggregatorUnknown error
-1002AggregatorInvalid transaction
-1003AggregatorTransaction not fully signed
-1004AggregatorInvalid block height
-2000RFQFailed to land
-2001RFQUnknown error
-2002RFQInvalid payload
-2003RFQQuote expired
-2004RFQSwap rejected