Developer

Jupiter API Guide: Swap V2, Price V3 & Trading Bots

Jupiter Swap API V2, Price API V3 and Tokens API V2 for Solana developers: order and execute flows, auth, rate limits and safe trading bot patterns.

Jupiter API Guide: Swap V2, Price V3 & Trading Bots article cover
In this article
  1. What You Will Learn
  2. Current Jupiter API Map
  3. Swap API V2: Managed Order and Execute
  4. Swap API V2: Custom Build Flow
  5. Price API V3
  6. Tokens API V2
  7. Amounts, Decimals, and Slippage
  8. Production Error Handling
  9. A Safe Jupiter Trading Bot Flow
  10. Common Solana Mint Addresses
  11. Key Takeaways

Jupiter is Solana's liquidity aggregator. A wallet, swap interface, or Jupiter trading bot can ask it for an executable route across available liquidity instead of integrating every venue separately. The current developer stack centers on Swap API V2, with Price API V3 for estimated token prices and Tokens API V2 for token metadata and discovery.

This guide was updated for the current api.jup.ag endpoints. Jupiter changes quickly, so pin your integration to the versioned path, validate every response, and confirm important limits against the official Jupiter developer documentation before shipping.


What You Will Learn

  • When to use the managed /order and /execute flow
  • When the custom /build flow is a better fit
  • How to request prices from Price API V3
  • Where Tokens API V2 fits into token search and discovery
  • How to handle raw token amounts, slippage, expiry, errors, and rate limits
  • How a production Solana trading bot should separate routing from its own risk controls

Current Jupiter API Map

APIBase pathPrimary job
Swap API V2https://api.jup.ag/swap/v2Find, build, and execute swap routes
Price API V3https://api.jup.ag/price/v3Estimate current token prices
Tokens API V2https://api.jup.ag/tokens/v2Search token metadata and token categories

Use the version in the URL as part of your integration contract. Do not silently mix fields or response assumptions from a different API generation.

Authentication and rate limits

Jupiter supports a small keyless allowance and higher developer tiers. The current keyless allowance is approximately 0.5 requests per second; authenticated limits depend on the tier and may change. When you have an API key, send it using the header documented by Jupiter:

const response = await fetch(url, {
  headers: {
    "x-api-key": process.env.JUPITER_API_KEY,
  },
});

Never ship a private Jupiter API key in browser JavaScript. Call Jupiter from your backend or local desktop process, and implement explicit handling for 401, 429, and 5xx responses.


Swap API V2: Managed Order and Execute

The managed flow is the direct starting point for most integrations:

  1. Request an order for the intended swap.
  2. Inspect the route, amounts, price impact, and transaction returned by Jupiter.
  3. Let the user or local wallet sign the transaction.
  4. Send the signed transaction to /execute with the original request identifier.
  5. Track the returned signature and final on-chain state.

Request an order

Endpoint: GET https://api.jup.ag/swap/v2/order

Common query parameters include:

ParameterMeaning
inputMintMint address of the token being sold
outputMintMint address of the token being bought
amountInput amount in the token's smallest unit
takerPublic key of the wallet that will sign

Example: request an order that swaps 0.1 SOL to USDC. SOL uses 9 decimals, so 100000000 lamports equals 0.1 SOL.

GET https://api.jup.ag/swap/v2/order?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000000&taker=YOUR_WALLET_PUBLIC_KEY
const jupiterHeaders = process.env.JUPITER_API_KEY
  ? { "x-api-key": process.env.JUPITER_API_KEY }
  : {};

const params = new URLSearchParams({
  inputMint: "So11111111111111111111111111111111111111112",
  outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  amount: "100000000",
  taker: walletPublicKey,
});

const orderResponse = await fetch(
  `https://api.jup.ag/swap/v2/order?${params}`,
  { headers: jupiterHeaders },
);

if (!orderResponse.ok) {
  throw new Error(`Jupiter order failed: ${orderResponse.status}`);
}

const order = await orderResponse.json();

Treat the response as untrusted input even when the transport succeeds. Before signing:

  • Confirm the input and output mints still match the user's intent.
  • Confirm the wallet and amount are correct.
  • Check estimated output, price impact, fees, and expiry-related fields.
  • Reject a route that violates your own slippage, liquidity, or risk policy.
  • Decode or simulate the transaction when your architecture supports it.

Sign the transaction

Jupiter returns a serialized transaction for the requested order. Decode the base64 bytes, deserialize the Solana transaction type expected by the response, and have the intended wallet sign it. A server should not sign for a self-custody user unless that server is explicitly the user's own signing system.

const transactionBytes = Buffer.from(order.transaction, "base64");

// Deserialize with the Solana SDK used by your application,
// present the transaction to the intended wallet, then serialize it again.
const signedTransaction = await signWithLocalWallet(transactionBytes);
const signedBase64 = Buffer.from(signedTransaction).toString("base64");

The exact deserialization call depends on whether the returned payload is a versioned transaction and which Solana SDK version you use. Do not guess: inspect the current Jupiter response contract and use the matching Solana transaction type.

Execute the signed order

Endpoint: POST https://api.jup.ag/swap/v2/execute

Send the signed transaction together with the requestId returned by /order.

const executeResponse = await fetch("https://api.jup.ag/swap/v2/execute", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    ...jupiterHeaders,
  },
  body: JSON.stringify({
    signedTransaction: signedBase64,
    requestId: order.requestId,
  }),
});

const execution = await executeResponse.json();

if (!executeResponse.ok) {
  throw new Error(execution.error || `Jupiter execute failed: ${executeResponse.status}`);
}

A successful HTTP response is not the same as a finalized Solana transaction. Record the signature, follow its confirmation state, and reconcile ambiguous timeouts against the chain before retrying. Blindly sending a second swap after a timeout can create a duplicate trade.


Swap API V2: Custom Build Flow

Use /build when your application needs more control than the managed order/execute path provides-for example, when composing instructions, controlling transaction submission, or integrating with a specialized execution pipeline.

Endpoint: GET https://api.jup.ag/swap/v2/build

The response contains the swap and supporting instructions plus address lookup-table information. Your application assembles those instructions into the final transaction, adds any permitted custom instructions, simulates the message, and then signs and submits it through its chosen execution path.

The custom flow shifts more responsibility to your application:

  • You must preserve the quoted intent while building the transaction.
  • You own signing, RPC submission, prioritization, confirmation, and retry behavior.
  • You must handle blockhash expiry and decide when a rebuild is required.
  • You must simulate and validate the final message, especially if you add instructions.

Do not choose /build merely because it appears lower level. The managed flow removes several failure modes and is usually easier to operate correctly. Choose the custom path only when you can name the control you need and implement the additional safeguards.


Price API V3

Price API V3 returns Jupiter's current price estimates for one or more token mints.

Endpoint: GET https://api.jup.ag/price/v3

GET https://api.jup.ag/price/v3?ids=So11111111111111111111111111111111111111112,JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN
const mints = [
  "So11111111111111111111111111111111111111112",
  "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
];

const priceResponse = await fetch(
  `https://api.jup.ag/price/v3?ids=${encodeURIComponent(mints.join(","))}`,
  { headers: jupiterHeaders },
);

if (!priceResponse.ok) {
  throw new Error(`Jupiter price request failed: ${priceResponse.status}`);
}

const prices = await priceResponse.json();

Price API is not a swap quote

A displayed price cannot promise the output of a trade. Executable output depends on amount, route liquidity, fees, price impact, and current on-chain state. Use Price API V3 for portfolio displays, rough valuation, and discovery context. Use Swap API V2 for an amount-specific route.

For a latency-sensitive trading system, also define the source of truth for live positions. ScreenerBot, for example, reads the selected pool's reserves directly for trading and P&L while keeping historical OHLCV data separate for indicators. Jupiter's route is used for execution, not substituted for every internal price decision.


Tokens API V2

Tokens API V2 supports token search and token-category workflows. It is useful when a wallet or trading bot needs metadata for a mint or wants to discover Jupiter's recent, trending, organic, or highly traded token sets.

The versioned base path is https://api.jup.ag/tokens/v2. Endpoint shapes within that API are purpose-specific, so select the current search or category endpoint from Jupiter's Tokens V2 reference instead of assuming one response model fits every list.

Typical uses include:

  • Resolve a mint to symbol, name, logo, and metadata.
  • Search by mint, symbol, or name.
  • Seed a discovery pipeline from a current token category.
  • Validate that UI labels belong to the mint the user selected.

Token metadata is descriptive, not proof of safety. Symbols and logos can be copied by malicious tokens. Keep the mint address visible, validate it throughout the request, and run independent on-chain and security checks before trading.


Amounts, Decimals, and Slippage

Always send raw integer amounts

API amounts represent the token's smallest unit. Convert a human amount only after loading that mint's decimals:

function toRawAmount(value, decimals) {
  const [whole, fraction = ""] = String(value).split(".");
  const padded = fraction.padEnd(decimals, "0").slice(0, decimals);
  return `${whole}${padded}`.replace(/^0+(?=\d)/, "");
}

toRawAmount("0.1", 9); // "100000000" lamports

Avoid floating-point multiplication for monetary conversion. A decimal library or exact string conversion prevents rounding errors for large values and tokens with many decimals.

Slippage is a limit, not a target

Your application should define a maximum acceptable outcome and reject a route outside it. Wider slippage may improve landing probability, but it also authorizes a worse fill. Tie the limit to liquidity, volatility, position size, and the user's explicit policy rather than using one universal percentage.

Price impact needs its own guard

Slippage and price impact answer different questions. Slippage limits movement between quote and execution; price impact estimates how much the trade itself moves through available liquidity. Inspect both before signing.


Production Error Handling

Distinguish retryable from terminal failures

FailureTypical response
400 validation errorFix the request; do not retry unchanged
401 authentication errorCheck the API key and environment
404 or no routeTreat as unavailable unless market state changes
429 rate limitBack off with jitter and respect response guidance
5xx provider errorRetry within a bounded policy
Execute timeoutCheck the signature or chain state before another trade
Expired transactionRequest a fresh order or rebuild; do not reuse stale bytes
async function fetchWithBackoff(url, options, maxAttempts = 4) {
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    const response = await fetch(url, options);

    if (response.ok || (response.status < 500 && response.status !== 429)) {
      return response;
    }

    if (attempt === maxAttempts) return response;

    const jitter = Math.floor(Math.random() * 250);
    await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 250 + jitter));
  }
}

Bound every retry loop. A trading bot should surface a degraded provider state instead of creating an unbounded queue of stale orders.

Cache only what can safely be stale

  • Token metadata can usually be cached longer.
  • Price displays can use a brief cache with a visible timestamp.
  • Orders and serialized transactions should be treated as short-lived.
  • Never reuse an old signed transaction for a new user intent.

Observe the full lifecycle

Log the request identifier, intended mints and raw amount, route summary, signature, confirmation state, and final error category. Do not log private keys, full secret-bearing headers, or sensitive wallet material.


A Safe Jupiter Trading Bot Flow

Jupiter solves routing, not the entire trading decision. A production workflow should remain explicit:

  1. Discover a token or receive a user-selected mint.
  2. Verify identity using the mint address, not just a symbol or logo.
  3. Inspect risk with on-chain authority, holder, liquidity, and security context.
  4. Apply strategy limits for position size, price impact, slippage, exposure, and loss.
  5. Request a current order from Swap API V2.
  6. Validate and sign only if the returned transaction matches the intended action.
  7. Execute once and reconcile ambiguous responses against Solana.
  8. Monitor the position using the price source and exit rules defined by the application.

ScreenerBot follows this separation of concerns: market sources help discover and filter candidates, local rules decide whether a trade is allowed, and Jupiter supplies the swap route. The integration does not outsource custody or risk policy to the routing API.


Common Solana Mint Addresses

TokenMint address
Wrapped SOLSo11111111111111111111111111111111111111112
USDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
JUPJUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN

Always obtain production mint addresses from an authoritative source and keep them configurable. A copied symbol is not an identifier.


Key Takeaways

  1. Use the versioned Swap API V2 base path at api.jup.ag/swap/v2.
  2. Start with /order and /execute; choose /build only when you need custom execution control.
  3. Use Price API V3 for estimates and Swap API V2 for executable, amount-specific routes.
  4. Use Tokens API V2 for metadata and discovery, never as a security verdict.
  5. Convert amounts with exact decimal logic and validate mints, output, impact, and slippage before signing.
  6. Reconcile timeouts against Solana before retrying an execution.
  7. Keep Jupiter routing separate from your wallet custody, strategy rules, and live-position price source.

For endpoint fields and current tier limits, use the official Jupiter API documentation as the final authority.

View all research