Skip to main content

Documentation Index

Fetch the complete documentation index at: https://dev.jup.ag/docs/llms.txt

Use this file to discover all available pages before exploring further.

The Meta-Aggregator is the recommended way to swap on Jupiter. All routing engines compete for the best price (Metis, JupiterZ RFQ, Dflow, OKX), and Jupiter handles transaction landing for you via /order + /execute.

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.
Adding optional parameters to /order (such as fee or slippage overrides) may restrict routing. See the routing impact matrix below for details.
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, see the API reference. For how each parameter affects routing, see the routing impact matrix below.

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.
/execute has its own dedicated rate limit bucket (Keyless 20, Free 50, Paid 100 RPS), separate from your general API limit. See Rate Limits.
Jupiter handles:
  • Optimised slippage via RTSE (Real-Time Slippage Estimator), applied at order 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

Routing impact matrix

Adding optional parameters to /order can restrict which routers are eligible:
ParameterMetisJupiterZ (RFQ)DflowOKX
(no optional params)YesYesYesYes
receiverYesNoYesYes
referralAccount & referralFeeYesNoYesYes
payer (integrator gasless)YesNoNoNo
excludeRouters: jupiterzYesNoYesYes
Key takeaway: adding receiver, referralAccount, referralFee or payer disables JupiterZ (RFQ), which may result in worse pricing on major pairs where market makers often beat onchain routing by 5-20bps. For the full parameter reference, see the API reference.

Order mode

The /order response includes a mode field that indicates whether optional parameters were applied that may affect routing or swap behaviour:
ModeMeaning
ultraNo optional params used. Default behaviour with all routers eligible.
manualOptional params detected (e.g. slippageBps, referralAccount, payer). These modifications may restrict routing or change swap behaviour.
mode does not indicate which router was used for the swap. It signals whether you adjusted parameters that could affect price or swap success. This is useful for debugging: if a swap fails in manual mode, the parameter modifications you applied may be the cause.
This is similar to how the jup.ag frontend behaves: when you use custom settings like slippage, priority fee strategy, or dex/router exclusions, the swap is handled differently. Since you opted into custom parameters, you take responsibility for the impact on swap outcomes.

Fees

Jupiter platform fee

Jupiter charges a platform fee on /order swaps. This fee is included in the quote and deducted automatically. The platformFee field in the response shows the fee amount and rate:
{
  "platformFee": {
    "amount": "8529",
    "feeBps": 5,
    "feeMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
  }
}

Total fee vs platform fee

The top-level feeBps field is the total fee rate charged for the swap. The platformFee.feeBps field is the Jupiter platform fee rate, which is the fee Jupiter intends to make from the swap. These values can differ when the swap includes additional charges, such as gasless support cost recoup. For example, if the platform fee for a swap pair is 5 bps and gasless support adds 7 bps, the response can show feeBps: 12 and platformFee.feeBps: 5.

Fee breakdown

The platform fee varies by token pair:
Token PairFee (bps)
Buying Jupiter tokens (SOL/Stable to JUP/JLP/jupSOL)0
Pegged assets (LST-LST, Stable-Stable)0
SOL-Stable2
LST-Stable5
Everything else10
New tokens (within 24 hours of token age)50

Fee mint priority

Jupiter determines which token to collect fees in based on a priority list:
  1. SOL
  2. Stablecoins (USDC, USDT, etc.)
  3. Liquid staked tokens (jupSOL, etc.)
  4. Bluechips (large market cap tokens)
  5. Others
Check the feeBps, platformFee.feeBps, and feeMint fields in the /order response to see the total fee rate, Jupiter platform fee rate, and fee token for your swap.

Referral fees

Use the Jupiter Referral Program to earn fees on /order swaps. This requires setting up referral accounts before you can collect fees.

How it works

  • Jupiter takes 20% of your integrator fee (no separate platform fee when referral is active)
  • Jupiter decides which token mint to collect fees in based on the fee mint priority list
  • You must create a referralTokenAccount for each mint you expect to receive fees in
  • If the referralTokenAccount for the feeMint is not initialised, the order still returns but executes without your fees (the user still gets their swap)
  • Fee range: 50 to 255 bps
  • Supports SPL and Token2022 tokens

Setup

Three one-time steps before you can collect fees:
  1. Install the Referral SDK
npm install @jup-ag/referral-sdk
  1. Create a referralAccount (once)
import { ReferralProvider } from "@jup-ag/referral-sdk";
import { Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js";
import bs58 from "bs58";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.BS58_PRIVATE_KEY!));
const provider = new ReferralProvider(connection);

// Jupiter Ultra Referral Project
const projectPubKey = new PublicKey("DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc");

const transaction = await provider.initializeReferralAccountWithName({
  payerPubKey: wallet.publicKey,
  partnerPubKey: wallet.publicKey,
  projectPubKey: projectPubKey,
  name: "your-app-name",
});

const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]);
console.log("referralAccount:", transaction.referralAccountPubKey.toBase58());
  1. Create referralTokenAccount for each fee mint
Create a token account for each mint you expect to collect fees in. Start with SOL and USDC. You can add more later.
const mint = new PublicKey("So11111111111111111111111111111111111111112"); // SOL

const transaction = await provider.initializeReferralTokenAccountV2({
  payerPubKey: wallet.publicKey,
  referralAccountPubKey: new PublicKey("YOUR_REFERRAL_ACCOUNT"),
  mint,
});

const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]);
console.log("referralTokenAccount:", transaction.tokenAccount.toBase58());
You can also use the Referral Dashboard to create accounts via a web interface.

Usage

Pass referralAccount and referralFee to /order:
const params = new URLSearchParams({
  inputMint: "So11111111111111111111111111111111111111112",
  outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  amount: "1000000000",
  taker: walletAddress,
  referralAccount: "YOUR_REFERRAL_ACCOUNT",
  referralFee: "50", // 50 bps = 0.5%
});

const response = await fetch(
  `https://api.jup.ag/swap/v2/order?${params}`,
  { headers: { "x-api-key": API_KEY } }
);
Verify your fees are applied by checking the response fee fields. Top-level feeBps is the total fee rate charged for the swap, while platformFee.feeBps is the Jupiter platform fee rate. If the response falls back to the default platform fee, the referralTokenAccount for that feeMint is likely not initialised.
Adding referralAccount disables RFQ routing. You will only get Metis quotes. See the routing impact matrix above.

Fee response fields

The /order response includes these fee-related fields:
FieldDescription
feeBpsTotal fee rate charged for the swap, including platform fee and additional charges such as gasless support cost recoup
feeMintToken fees are collected in
platformFee.amountJupiter platform fee amount
platformFee.feeBpsJupiter platform fee rate, excluding gasless support cost recoup
referralAccountReferral account used (if set)