Skip to main content

Overview

Metis is Jupiter’s low-level swap routing engine. It exposes granular control over every aspect of a swap transaction: route selection, instruction composition, priority fees, compute unit limits, slippage, and broadcasting. Unlike Jupiter Ultra, Metis requires you to handle these yourself — but in return you get maximum flexibility for integrations that need CPI, custom instructions, or a specific transaction broadcasting strategy. Use Metis when you need:
  • CPI (Cross Program Invocation) with your own on-chain program
  • Custom instruction composition around the swap
  • Full control over priority fees, compute units, and slippage
  • Your own RPC or Jito broadcasting
Consider Ultra instead when:
  • You want end-to-end execution without managing slippage, fees, or broadcasting
  • You need Real-Time Slippage Estimator (RTSE), only available via Ultra
Metis Swap API access is available on OrbitFlare Dedicated Nodes via the /jup path prefix on your node endpoint. For the hosted Jupiter API, use https://api.jup.ag/swap/v1/... with your x-api-key.

How It Works

1

Get Quote

Call GET /swap/v1/quote with the input mint, output mint, amount, and slippage to receive the best route from the Metis routing engine.
2

Build Swap Transaction

Post the quote response to POST /swap/v1/swap. The API returns a serialized, ready-to-sign transaction with optional priority fee estimation, dynamic compute unit limits, and dynamic slippage.
3

Sign and Send

Deserialize the base64 transaction, sign it with your wallet, and broadcast via connection.sendRawTransaction() or through Jito.

Step 1 — Get Quote

Endpoint: GET /swap/v1/quote

Required Parameters

ParameterDescription
inputMintToken mint address of the input token (e.g. So11111111111111111111111111111111111111112 for SOL)
outputMintToken mint address of the output token (e.g. EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v for USDC)
amountRaw integer amount of input tokens before decimals (lamports for SOL, atomic units for all others)
slippageBpsMaximum slippage tolerance in basis points (e.g. 50 = 0.5%)

Example — Quote 1 SOL to USDC

const quoteResponse = await (
  await fetch(
    "https://api.jup.ag/swap/v1/quote?" +
      "inputMint=So11111111111111111111111111111111111111112" +
      "&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" +
      "&amount=100000000" +
      "&slippageBps=50" +
      "&restrictIntermediateTokens=true",
    {
      headers: {
        "x-api-key": "YOUR_API_KEY",
      },
    }
  )
).json();

console.log(JSON.stringify(quoteResponse, null, 2));

Example Response

{
  "inputMint": "So11111111111111111111111111111111111111112",
  "inAmount": "100000000",
  "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "outAmount": "16198753",
  "otherAmountThreshold": "16117760",
  "swapMode": "ExactIn",
  "slippageBps": 50,
  "platformFee": null,
  "priceImpactPct": "0",
  "routePlan": [
    {
      "swapInfo": {
        "ammKey": "5BKxfWMbmYBAEWvyPZS9esPducUba9GqyMjtLCfbaqyF",
        "label": "Meteora DLMM",
        "inputMint": "So11111111111111111111111111111111111111112",
        "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "inAmount": "100000000",
        "outAmount": "16198753"
      },
      "percent": 100
    }
  ],
  "contextSlot": 299283763,
  "timeTaken": 0.015257836
}
outAmount is the best possible output at time of quote. slippageBps does not affect this value — it controls otherAmountThreshold, which is the minimum you will accept.

Additional Quote Options

Set to true to ensure routing only goes through highly liquid intermediate tokens. This reduces routing failures caused by illiquid paths and improves route stability at a small potential cost to price.
Set to true to restrict routing to a single market (no multi-hop). Returns no quote if no direct route exists. Can yield unfavorable prices — use with caution.
Limits the total number of accounts in the inner swap instructions. Useful when you need to leave room for your own instructions. Recommended minimum is 64. Setting it too low drops DEXes that require more accounts (e.g. Meteora DLMM requires up to 47).
Add a fee to the swap that is credited to your feeAccount. Combine with feeAccount in the swap request.

Step 2 — Build Swap Transaction

Endpoint: POST /swap/v1/swap Pass the quote response from Step 1 along with the user’s public key. The API returns a serialized base64 transaction ready to sign and send.

Build Transaction with Landing Optimizations

const swapResponse = await (
  await fetch("https://api.jup.ag/swap/v1/swap", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      quoteResponse,
      userPublicKey: wallet.publicKey.toString(),

      // Optimize for transaction landing
      dynamicComputeUnitLimit: true,
      dynamicSlippage: true,
      prioritizationFeeLamports: {
        priorityLevelWithMaxLamports: {
          maxLamports: 1000000,
          priorityLevel: "veryHigh",
        },
      },
    }),
  })
).json();

Example Response

{
  "swapTransaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAGDkS+...",
  "lastValidBlockHeight": 279632475,
  "prioritizationFeeLamports": 9999,
  "computeUnitLimit": 388876,
  "prioritizationType": {
    "computeBudget": {
      "microLamports": 25715,
      "estimatedMicroLamports": 785154
    }
  },
  "dynamicSlippageReport": {
    "slippageBps": 50,
    "otherAmount": 20612318,
    "simulatedIncurredSlippageBps": -18,
    "amplificationRatio": "1.5",
    "categoryName": "lst",
    "heuristicMaxSlippageBps": 100
  },
  "simulationError": null
}

Priority Fee Levels

priorityLevelPercentileUse Case
medium25thLow congestion, non-time-sensitive
high50thStandard trading conditions
veryHigh75thCompetitive conditions, MEV, time-sensitive swaps
Set maxLamports as a safety cap to prevent overpaying during fee spikes:
prioritizationFeeLamports: {
  priorityLevelWithMaxLamports: {
    maxLamports: 10000000,
    global: false,        // use local fee market (writable accounts), not global
    priorityLevel: "veryHigh",
  },
}

Using Swap Instructions Instead

If you need to compose the swap with your own instructions, use /swap/v1/swap-instructions instead of /swap/v1/swap. It takes the same parameters but returns individual instruction objects rather than a serialized transaction:
const instructions = await (
  await fetch("https://api.jup.ag/swap/v1/swap-instructions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": "YOUR_API_KEY",
    },
    body: JSON.stringify({
      quoteResponse,
      userPublicKey: wallet.publicKey.toString(),
    }),
  })
).json();

const {
  computeBudgetInstructions,
  setupInstructions,
  swapInstruction: swapInstructionPayload,
  cleanupInstruction,
  addressLookupTableAddresses,
} = instructions;

Step 3 — Send Swap Transaction

Deserialize, Sign, and Serialize

The swapTransaction field in the response is base64-encoded. Deserialize it to a VersionedTransaction, sign it, then convert back to binary for sending:
import { VersionedTransaction } from "@solana/web3.js";

const transactionBase64 = swapResponse.swapTransaction;
const transaction = VersionedTransaction.deserialize(
  Buffer.from(transactionBase64, "base64")
);

transaction.sign([wallet]);

const transactionBinary = transaction.serialize();

Send the Transaction

const signature = await connection.sendRawTransaction(transactionBinary, {
  maxRetries: 2,
  skipPreflight: true,
});
OptionDescription
maxRetriesMaximum retries before giving up. If omitted, the RPC retries until the blockhash expires.
skipPreflightSkip signature verification and simulation before submitting. Recommended true for speed-sensitive swaps.

Confirm the Transaction

const confirmation = await connection.confirmTransaction(
  { signature },
  "finalized"
);

if (confirmation.value.err) {
  throw new Error(
    `Transaction failed: ${JSON.stringify(confirmation.value.err)}\nhttps://solscan.io/tx/${signature}/`
  );
} else {
  console.log(`Transaction successful: https://solscan.io/tx/${signature}/`);
}

Broadcasting via Jito

To use Jito for faster inclusion and MEV protection, replace prioritizationFeeLamports with a Jito tip and submit to a Jito RPC endpoint:
body: JSON.stringify({
  quoteResponse,
  userPublicKey: wallet.publicKey.toString(),
  prioritizationFeeLamports: {
    jitoTipLamports: 1000000, // fixed lamports, not a cap
  },
}),
Jito tips must be submitted to a Jito RPC endpoint to take effect. Sending to a standard RPC will not route through Jito’s block engine.

Advanced

Enable dynamicSlippage: true in the swap request. The backend simulates the transaction and applies heuristics based on token category to estimate an appropriate slippage value.Note: Dynamic Slippage development has been discontinued. For production use, Jupiter recommends the Ultra Swap API which includes Real-Time Slippage Estimator (RTSE) — a significantly more accurate and reactive system.
As of January 2025, Jupiter Swap via CPI is the recommended method for on-chain program integration. Add the jupiter-cpi crate to your program:
[dependencies]
jupiter-cpi = { git = "https://github.com/jup-ag/jupiter-cpi", rev = "5eb8977" }
Then invoke the shared accounts route from your instruction handler. See the Jupiter CPI example for a full reference implementation.
Flash Fill was an earlier workaround for CPI account size limitations using Versioned Transactions and Address Lookup Tables. It is no longer recommended now that Loosen CPI restrictions are live on Solana mainnet. See sol-swap-flash-fill for historical reference.
When using maxAccounts, these are the estimated account counts per DEX (min applies when ALTs are available, max when they are not):
DEXMax AccountsMin Accounts
Meteora DLMM4719
Meteora4518
Raydium4518
Raydium CLMM4519
Moonshot3715
Raydium CPMM3714
Pumpfun AMM4217
Pumpfun Bonding Curve4016
Orca Whirlpool3012
Obric3012
Solfi229
Sanctum8080
Sanctum Infinity8080
Setting maxAccounts too low will silently drop DEXes from routing. Keep it as high as possible and only reduce if your transaction exceeds the 1232-byte size limit.

Requirements & Resources

Metis Swap API access via OrbitFlare is available on Dedicated Nodes only. The API is accessible via the /jup path on your dedicated node endpoint, or directly at https://api.jup.ag/swap/v1/... with a Jupiter API key.