# API Reference Source: https://dev.jup.ag/api-reference/index Overview of Jupiter API Reference 6 items 4 items 1 item 5 items 6 items 2 items 2 items 4 items 5 items # Lend API Source: https://dev.jup.ag/api-reference/lend Overview of Jupiter Lend API 11 items # Earn (Beta) Source: https://dev.jup.ag/api-reference/lend/earn Overview of Jupiter Lend Earn API Request for a base64-encoded unsigned earn deposit transaction to deposit assets Request for a base64-encoded unsigned earn withdraw transaction to withdraw assets Request for a base64-encoded unsigned earn mint transaction to mint shares Request for a base64-encoded unsigned earn redeem transaction to redeem shares Request for the instruction of an earn deposit transaction to deposit assets Request for the instruction of an earn withdraw transaction to withdraw assets Request for the instruction of an earn mint transaction to mint shares Request for the instruction of an earn redeem transaction to redeem shares Request for the tokens available to be deposited and their information Request for the position data of one or multiple users Request for the earnings of one or multiple posi # deposit Source: https://dev.jup.ag/api-reference/lend/earn/deposit openapi-spec/lend/lend.yaml post /earn/deposit Request for a base64-encoded unsigned earn deposit transaction to deposit assets # deposit-instructions Source: https://dev.jup.ag/api-reference/lend/earn/deposit-instructions openapi-spec/lend/lend.yaml post /earn/deposit-instructions Request for the instruction of an earn deposit transaction to deposit assets # earnings Source: https://dev.jup.ag/api-reference/lend/earn/earnings openapi-spec/lend/lend.yaml get /earn/earnings Request for the earnings of one or multiple positions of a user # mint Source: https://dev.jup.ag/api-reference/lend/earn/mint openapi-spec/lend/lend.yaml post /earn/mint Request for a base64-encoded unsigned earn mint transaction to mint shares # mint-instructions Source: https://dev.jup.ag/api-reference/lend/earn/mint-instructions openapi-spec/lend/lend.yaml post /earn/mint-instructions Request for the instruction of an earn mint transaction to mint shares # positions Source: https://dev.jup.ag/api-reference/lend/earn/positions openapi-spec/lend/lend.yaml get /earn/positions Request for the position data of one or multiple users # redeem Source: https://dev.jup.ag/api-reference/lend/earn/redeem openapi-spec/lend/lend.yaml post /earn/redeem Request for a base64-encoded unsigned earn redeem transaction to redeem shares # redeem-instructions Source: https://dev.jup.ag/api-reference/lend/earn/redeem-instructions openapi-spec/lend/lend.yaml post /earn/redeem-instructions Request for the instruction of an earn redeem transaction to redeem shares # tokens Source: https://dev.jup.ag/api-reference/lend/earn/tokens openapi-spec/lend/lend.yaml get /earn/tokens Request for the tokens available to be deposited and their information # withdraw Source: https://dev.jup.ag/api-reference/lend/earn/withdraw openapi-spec/lend/lend.yaml post /earn/withdraw Request for a base64-encoded unsigned earn withdraw transaction to withdraw assets # withdraw-instructions Source: https://dev.jup.ag/api-reference/lend/earn/withdraw-instructions openapi-spec/lend/lend.yaml post /earn/withdraw-instructions Request for the instruction of an earn withdraw transaction to withdraw assets # Price API Source: https://dev.jup.ag/api-reference/price Overview of Jupiter Price API 1 item 1 item # Price API V2 (Deprecated) Source: https://dev.jup.ag/api-reference/price/v2 Overview of Jupiter Price API V2 (Deprecated). Returns prices of specified tokens. # price Source: https://dev.jup.ag/api-reference/price/v2/price openapi-spec/price/v2/price.yaml get / Returns prices of specified tokens. Price V2 API is deprecated, please use Price V3 API instead. # Price API V3 (Beta) Source: https://dev.jup.ag/api-reference/price/v3 Overview of Jupiter Price API V3 (Beta). Returns prices of specified tokens. # price Source: https://dev.jup.ag/api-reference/price/v3/price openapi-spec/price/v3/price.yaml get /price/v3 Returns prices of specified tokens. # Recurring API Source: https://dev.jup.ag/api-reference/recurring Overview of Jupiter Recurring API Request for a base64-encoded unsigned recurring order creation transaction to be used in `POST /recurring/v1/execute` Execute the signed transaction and get the execution status Request for a base64-encoded unsigned recurring order cancellation transaction to be used in `POST /recurring/v1/execute` **DEPRECATED**: This endpoint is deprecated. Please use time-based recurring orders instead. **DEPRECATED**: This endpoint is deprecated. Please use time-based recurring orders instead. Request for the active or historical orders associated to the provided account # cancelOrder Source: https://dev.jup.ag/api-reference/recurring/cancel-order openapi-spec/recurring/recurring.yaml post /cancelOrder Request for a base64-encoded unsigned recurring order cancellation transaction to be used in `POST /recurring/v1/execute` **NOTE** * `recurringType` is used to denote the type of recurring order, only `time` * **DEPRECATED**: `recurringType: price` based orders are deprecated * Refer to [Recurring API doc](/docs/recurring/cancel-order) for more information. # createOrder Source: https://dev.jup.ag/api-reference/recurring/create-order openapi-spec/recurring/recurring.yaml post /createOrder Request for a base64-encoded unsigned recurring order creation transaction to be used in `POST /recurring/v1/execute` **NOTE** * Pass in the correct recurring type in the `params` field, only `time` * **DEPRECATED**: `params.price` based orders are deprecated * Refer to [Recurring API doc](/docs/recurring/create-order) for more information. # execute Source: https://dev.jup.ag/api-reference/recurring/execute openapi-spec/recurring/recurring.yaml post /execute Execute the signed transaction and get the execution status # getRecurringOrders Source: https://dev.jup.ag/api-reference/recurring/get-recurring-orders openapi-spec/recurring/recurring.yaml get /getRecurringOrders **NOTE** * `recurringType` is used to denote the type of recurring order, only `time` * **DEPRECATED**: `recurringType: price` based orders are deprecated # priceDeposit Source: https://dev.jup.ag/api-reference/recurring/price-deposit openapi-spec/recurring/recurring.yaml post /priceDeposit Request for a base64-encoded unsigned price-based recurring order deposit transaction to be used in `POST /recurring/v1/execute`. Price-based recurring orders are deprecated, please use time-based recurring orders instead. # priceWithdraw Source: https://dev.jup.ag/api-reference/recurring/price-withdraw openapi-spec/recurring/recurring.yaml post /priceWithdraw Request for a base64-encoded unsigned price-based recurring order withdrawal transaction to be used in `POST /recurring/v1/execute`. Price-based recurring orders are deprecated, please use time-based recurring orders instead. # Send API Source: https://dev.jup.ag/api-reference/send Overview of Jupiter Send API Request for a base64-encoded unsigned Send transaction Request for a base64-encoded unsigned Send transaction Request for the pending invites of an address Request for the invite history of an address # craft-clawback Source: https://dev.jup.ag/api-reference/send/craft-clawback openapi-spec/send/send.yaml post /craft-clawback Request for a base64-encoded unsigned Send transaction # craft-send Source: https://dev.jup.ag/api-reference/send/craft-send openapi-spec/send/send.yaml post /craft-send Request for a base64-encoded unsigned Send transaction # invite-history Source: https://dev.jup.ag/api-reference/send/invite-history openapi-spec/send/send.yaml get /invite-history Request for the invite history of an address # pending-invites Source: https://dev.jup.ag/api-reference/send/pending-invites openapi-spec/send/send.yaml get /pending-invites Request for the pending invites of an address # Studio API Source: https://dev.jup.ag/api-reference/studio Overview of Jupiter Studio API Request for a base64-encoded unsigned transaction to create a Dynamic Bonding Curve pool with token metadata Execute the signed transaction, and optionally upload content and header image Request for pool addresses for a given token mint Request for unclaimed creator trading fees of a Dynamic Bonding Curve pool Request for a base64-encoded unsigned transaction to claim creator trading fees of a Dynamic Bonding Curve pool # dbc-fee Source: https://dev.jup.ag/api-reference/studio/dbc-fee openapi-spec/studio/studio.yaml post /dbc/fee Request for unclaimed creator trading fees of a Dynamic Bonding Curve pool # dbc-fee-create-tx Source: https://dev.jup.ag/api-reference/studio/dbc-fee-create-tx openapi-spec/studio/studio.yaml post /dbc/fee/create-tx Request for a base64-encoded unsigned transaction to claim creator trading fees of a Dynamic Bonding Curve pool - Handles both direct creator ownership and proxy-based ownership. # dbc-pool-addresses-by-mint Source: https://dev.jup.ag/api-reference/studio/dbc-pool-addresses-by-mint openapi-spec/studio/studio.yaml get /dbc-pool/addresses/{mint} Request for pool addresses for a given token mint # dbc-pool-create-tx Source: https://dev.jup.ag/api-reference/studio/dbc-pool-create-tx openapi-spec/studio/studio.yaml post /dbc-pool/create-tx Request for a base64-encoded unsigned transaction to create a Dynamic Bonding Curve pool with token metadata # dbc-pool-submit Source: https://dev.jup.ag/api-reference/studio/dbc-pool-submit openapi-spec/studio/studio.yaml post /dbc-pool/submit Execute the signed transaction, and optionally upload content and header image # Legacy Swap API Source: https://dev.jup.ag/api-reference/swap Overview of Jupiter Legacy Swap API Request for a quote to be used in `POST /swap` Request for a base64-encoded unsigned swap transaction based on the `/quote` response Request for swap instructions that you can use from the quote you get from `/quote` Returns a hash, which key is the program id and value is the label. # program-id-to-label Source: https://dev.jup.ag/api-reference/swap/program-id-to-label openapi-spec/swap/swap.yaml get /program-id-to-label Returns a hash, which key is the program id and value is the label. This is used to help map error from transaction by identifying the fault program id. This can be used in conjunction with the `excludeDexes` or `dexes` parameter. # quote Source: https://dev.jup.ag/api-reference/swap/quote openapi-spec/swap/swap.yaml get /quote Request for a quote to be used in `POST /swap` # swap Source: https://dev.jup.ag/api-reference/swap/swap openapi-spec/swap/swap.yaml post /swap Request for a base64-encoded unsigned swap transaction based on the `/quote` response # swap-instructions Source: https://dev.jup.ag/api-reference/swap/swap-instructions openapi-spec/swap/swap.yaml post /swap-instructions Request for swap instructions that you can use from the quote you get from `/quote` # Tokens API Source: https://dev.jup.ag/api-reference/tokens Overview of Jupiter Tokens API 4 items 6 items # Tokens API V1 (Deprecated) Source: https://dev.jup.ag/api-reference/tokens/v1 Overview of Jupiter Tokens API V1 (Deprecated). Returns the specified mint address's token information and metadata. Returns the mints involved in a market. Returns a list of all mints tradable via Jupiter routing. Returns a list of mints with specified tag(s) along with their metadata. Returns new tokens with metadata, created at timestamp and markets. Returns all tokens with all metadata. # all Source: https://dev.jup.ag/api-reference/tokens/v1/all openapi-spec/tokens/v1/tokens.yaml get /all Returns all tokens with all metadata. Tokens API V1 is deprecated, please use Tokens API V2 instead. # mints in market Source: https://dev.jup.ag/api-reference/tokens/v1/mints-in-market openapi-spec/tokens/v1/tokens.yaml get /market/{market_address}/mints Returns the mints involved in a market. Tokens API V1 is deprecated, please use Tokens API V2 instead. # new Source: https://dev.jup.ag/api-reference/tokens/v1/new openapi-spec/tokens/v1/tokens.yaml get /new Returns new tokens with metadata, created at timestamp and markets. Tokens API V1 is deprecated, please use Tokens API V2 instead. # tagged Source: https://dev.jup.ag/api-reference/tokens/v1/tagged openapi-spec/tokens/v1/tokens.yaml get /tagged/{tag} Returns a list of mints with specified tag(s) along with their metadata. Tokens API V1 is deprecated, please use Tokens API V2 instead. # token information Source: https://dev.jup.ag/api-reference/tokens/v1/token-information openapi-spec/tokens/v1/tokens.yaml get /token/{mint_address} Returns the specified mint address's token information and metadata. Tokens API V1 is deprecated, please use Tokens API V2 instead. # tradable Source: https://dev.jup.ag/api-reference/tokens/v1/tradable openapi-spec/tokens/v1/tokens.yaml get /mints/tradable Returns a list of all mints tradable via Jupiter routing. Tokens API V1 is deprecated, please use Tokens API V2 instead. # Tokens API V2 (Beta) Source: https://dev.jup.ag/api-reference/tokens/v2 Overview of Jupiter Tokens API V2 (Beta). Request a search by token's symbol, name or mint address Request an array of mints and their information by a tag Returns an array of mints and their information for the given category and interval Returns an array of mints that recently had their **first created pool** # category Source: https://dev.jup.ag/api-reference/tokens/v2/category openapi-spec/tokens/v2/tokens.yaml get /{category}/{interval} Returns an array of mints and their information for the given category and interval # null Source: https://dev.jup.ag/api-reference/tokens/v2/recent openapi-spec/token/v2/token.yaml get /recent # search Source: https://dev.jup.ag/api-reference/tokens/v2/search openapi-spec/tokens/v2/tokens.yaml get /search Request a search by token's symbol, name or mint address # tag Source: https://dev.jup.ag/api-reference/tokens/v2/tag openapi-spec/tokens/v2/tokens.yaml get /tag Request an array of mints and their information by a tag - Note that this will return the entire array of existing mints that belongs to the tag. # Trigger API Source: https://dev.jup.ag/api-reference/trigger Overview of Jupiter Trigger API Request for a base64-encoded unsigned trigger order creation transaction to be used in `POST /trigger/v1/execute` Execute the signed transaction and get the execution status Request for a base64-encoded unsigned trigger order cancellation transaction to be used in `POST /trigger/v1/execute` Request for a base64-encoded unsigned trigger order cancellation transaction(s) to be used in `POST /trigger/v1/execute` Request for the active or historical orders associated to the provided account # cancelOrder Source: https://dev.jup.ag/api-reference/trigger/cancel-order openapi-spec/trigger/trigger.yaml post /cancelOrder Request for a base64-encoded unsigned trigger order cancellation transaction to be used in `POST /trigger/v1/execute` # cancelOrders Source: https://dev.jup.ag/api-reference/trigger/cancel-orders openapi-spec/trigger/trigger.yaml post /cancelOrders Request for a base64-encoded unsigned trigger order cancellation transaction(s) to be used in `POST /trigger/v1/execute` # createOrder Source: https://dev.jup.ag/api-reference/trigger/create-order openapi-spec/trigger/trigger.yaml post /createOrder Request for a base64-encoded unsigned trigger order creation transaction to be used in `POST /trigger/v1/execute` # execute Source: https://dev.jup.ag/api-reference/trigger/execute openapi-spec/trigger/trigger.yaml post /execute Execute the signed transaction and get the execution status **NOTE** * Do note that the `requestId` is found in the response of `/createOrder` or `/cancelOrder` # getTriggerOrders Source: https://dev.jup.ag/api-reference/trigger/get-trigger-orders openapi-spec/trigger/trigger.yaml get /getTriggerOrders Request for the active or historical orders associated to the provided account # Ultra Swap API Source: https://dev.jup.ag/api-reference/ultra Overview of Jupiter Ultra Swap API Request for a base64-encoded unsigned swap transaction to be used in `POST /ultra/v1/execute` Execute the signed transaction and get the execution status Request for token balances of an account Request for token information and warnings of mints Request for token balances of an account Request a search by token's symbol, name or mint address Request for the list of routers available in the routing engine of Ultra Swap, which is [Juno](/docs/routing#juno-liquidity-engine) # null Source: https://dev.jup.ag/api-reference/ultra/balances openapi-spec/ultra/ultra.yaml get /balances/{address} # null Source: https://dev.jup.ag/api-reference/ultra/execute openapi-spec/ultra/ultra.yaml post /execute **NOTE** * The `requestId` is found in the response of `/order` # null Source: https://dev.jup.ag/api-reference/ultra/holdings openapi-spec/ultra/ultra.yaml get /holdings/{address} # null Source: https://dev.jup.ag/api-reference/ultra/order openapi-spec/ultra/ultra.yaml get /order # null Source: https://dev.jup.ag/api-reference/ultra/routers openapi-spec/ultra/ultra.yaml get /order/routers # null Source: https://dev.jup.ag/api-reference/ultra/search openapi-spec/ultra/ultra.yaml get /search # null Source: https://dev.jup.ag/api-reference/ultra/shield openapi-spec/ultra/ultra.yaml get /shield # Overview Source: https://dev.jup.ag/blog/index Technical deep dives into Jupiter products, team's research and learnings Ultra V3 represents the culmination of months of infrastructure work, algorithm optimization, and real-world testing under production load. This release fundamentally changes how we approach swap execution, with measurable improvements across every aspect that matters - read more to find out.
Ultra Swap Oct 15, 2025
Stay tuned for more technical insights and updates from Jupiter # Ultra Swap V3 Source: https://dev.jup.ag/blog/ultra-v3 A Technical Deep Dive into Best-in-Class Execution Ultra Swap V3 Ultra V3 represents the culmination of months of infrastructure work, algorithm optimization, and real-world testing under production load. This release fundamentally changes how we approach swap execution, with measurable improvements across every aspect that matters:
* [Sub-Second Transaction Landing](#sub-second-transaction-landing) * [MEV Protection](#mev-protection) * [Just-In-Time Quote Simulation](#just-in-time-quote-simulation) * [Just-In-Time Market Revival](#just-in-time-market-revival) * [Pre-graduation Routing Latency Reduction](#pre-graduation-routing-latency-reduction) * [Slippage Optimization](#slippage-optimization) * [Gasless Support Coverage](#gasless-support-coverage)
## Sub-Second Transaction Landing IronJup is Jupiter’s proprietary transaction landing infrastructure, designed for measurable improvements in both speed and reliability. ### Transaction Landing Latency Transaction landing latency has improved by **50–66%** compared with our previous approach that relied on multiple providers. | Method | Blocks | Latency | | ----------------------- | ---------- | --------------- | | **IronJup** | 0-1 block | \~ 50ms - 400ms | | **Traditional methods** | 1-3 blocks | \~ 400ms - 1.2s | Every block your transaction waits is an exposure to poorer execution quality, such as market movement against your position, increased effective slippage, and a higher risk of MEV extraction opportunities. With IronJup we now leverage our own validator stake and dedicated R\&D efforts, IronJup consistently processes transactions within 50–400 ms and by operating entirely on our own infrastructure instead of relying on external providers, we eliminate the risk of artificial delays and front-running, ensuring faster and more secure execution for our users. ### MEV Protection IronJup routes transactions through our own infrastructure, providing: * **Complete transaction privacy** until on-chain execution. * **Reduce frontrunning exposure** - transactions are invisible to public mempool scanners. * **Reduce risk of sandwich attack vectors** - see [sandwiched.me](https://sandwiched.me/sandwiches) for MEV analytics. Jupiter Ultra's swap volume to value extracted ratio is significantly lower than others despite higher volume, demonstrating the effectiveness of our efforts to bring the best execution quality to our users. This redundancy ensures optimal performance even when individual methods experience degraded service, contributing to our 96% success rate.
Source: Sandwiched.me Oct 15, 2025
## Just-In-Time Quote Simulation A problem that is often overlooked is the lack of comparison between **Quoted Price versus Executed Price**. When a quote is provided, it does not necessarily represent the actual executable price and quality. ### Executed Price versus Quoted Price In today's Solana DeFi landscape dominated by proprietary AMMs, these two numbers are increasingly divergent. Consider this scenario: | Route | Quoted Amount | Executed Amount | Slippage (Amount) | Slippage (%) | | ----------------------------- | ------------- | --------------- | ----------------- | ------------ | | **Route A (Traditional AMM)** | 100 tokens | 80 tokens | 20 tokens | 20% | | **Route B (Prop AMM)** | 150 tokens | 50 tokens | 100 tokens | 66% | Route B shows a 50% better quote, but Route A delivers 60% more tokens to your wallet. This is why quote comparison between platforms is fundamentally misleading - but we have made significant improvements to our routing engine to ensure that the provided quotes have verifiable executable prices. ### Verifiable Executable Price Ultra V3 introduces **Just-In-Time Simulation** for every quote returned by every router we aggregate. **How it works:** 1. Aggregated routers return their best quotes. 2. Ultra performs on-chain simulation of each route before returning quotes to users. 3. Simulations verify actual executable price vs quoted price. 4. Routes are ranked by simulated outcome, not just quoted price. **Impact:** Users receive quotes that reflect actual on-chain execution, not theoretical best-case scenarios. This shift from quote-optimized to execution-optimized routing is responsible for our swap success rate improvement from **85% to \~96%**. ## Just-In-Time Market Revival Ultra V3 solves a long-standing technical limitation which is to provide routes for tokens that have "fallen out of favor" - When markets are have low liquidity or yet to graduate for a long time, we downgrade these markets to optimize resource allocation. This makes them unroutable, preventing users to enter or exit their positions. To address this, we have implemented a Just-In-Time market Revival Mechanism that **dynamically re-indexes token markets on-demand.** * Routes virtually any token on Solana, including extremely long-tail assets. * Supports old, inactive tokens that historically failed to route. * Essentially, no minimum liquidity threshold for token eligibility. This behavior is exclusive to Ultra only. Legacy Swap API does not have this capability. ## Pre-Graduation Routing Latency Reduction To further enhance the trading experience for trenchers, we have optimized the routing logic for all pre-graduated bonding curve markets by skipping multi-route aggregation to reduce the quote latency by 90%+: | | Average Quote Latency | Routing Method | | ------ | --------------------- | --------------------------------------- | | Before | 200ms | Checking multiple potential routes | | After | 10-15ms | Direct routing to pre-graduated markets | ## Slippage Optimization Since the inception of RTSE (Real-Time Slippage Estimator), we have made strides to improve the slippage protection for our users. With Ultra V3, we have implemented the following improvements: * **Reduced RTSE slippage set** while maintaining high success rates. * **Automatic prioritization** of slippage-protected routes over purely price-optimized routes. * **Increased volatility sensitivity** for tokens with high historical volatility patterns. ## Gasless Support Coverage With Ultra V3, we have expanded gasless support coverage to Token2022 tokens, memecoin-to-memecoin swaps (when liquidity permits), and reduced minimum trade size to \~\$10 USD. We will continue to improve this over time. ## Jupiter Ultra Fee Structure We have the best quoting, routing, and execution infrastructure, yet Jupiter Ultra fee remains competitive and transparent. | Provider | Fee (bps) | Example Fee on \$10,000 Trade | Notes | | ----------------- | ------------------------ | ----------------------------- | ----------------------------------------------- | | **Jupiter Ultra** | 0 - 10 | `$0 – $10` | 10x cheaper | | **Competitors** | At least 85 - 100 (≥ 1%) | `$85 – $100` | Typically at least 1%; costs compound over time | ## Summary Ultra is more than just a swap - it's an abstraction layer over the challenges of execution and blockchain complexity: **For end users:** No need to understand slippage settings, gas fees, RPC selection, or MEV protection. Ultra handles everything. **For developers:** The exact same Ultra infrastructure that powers jup.ag's UI is also available to developers via API. No need to build, host, or maintain your own RPC, transaction landing infrastructure and manage other trading optimizations. For more information, please refer to the [Ultra Swap Docs](/docs/ultra). Ultra V3 demonstrates that the best execution isn't about finding the best quote - it's about delivering the best outcome. Through rigorous measurement, continuous optimization, and proprietary infrastructure, we've built a system that consistently outperforms on the metrics that actually matter - the data speaks for itself. # Borrow (Soon) Source: https://dev.jup.ag/docs/lend/borrow **WARNING** The Jupiter Lend Borrow API is still a **work in progress**, stay tuned! # Earn (Beta) Source: https://dev.jup.ag/docs/lend/earn Overview of Jupiter Lend Earn API. **NOTE** * Lite URL: `https://lite-api.jup.ag/lend/v1/earn` * Pro URL: `https://api.jup.ag/lend/v1/earn` To upgrade to Pro or understand our rate limiting, please refer to this section. **API REFERENCE** To fully utilize the Lend API, check out the [Lend API Reference](/api-reference/lend). ## Prerequisite ```bash theme={null} npm install @solana/web3.js@1 # Using v1 of web3.js instead of v2 npm install dotenv # If required for wallet setup ``` **Set up RPC** **NOTE** Solana provides a [default RPC endpoint](https://solana.com/docs/core/clusters). However, as your application grows, we recommend you to always use your own or provision a 3rd party provider’s RPC endpoint such as [Helius](https://helius.dev/) or [Triton](https://triton.one/). ```js theme={null} import { Connection } from "@solana/web3.js"; const connection = new Connection('https://api.mainnet-beta.solana.com'); ``` **Set up Development Wallet** **NOTE** * You can paste in your private key for testing purposes but this is not recommended for production applications. * If you want to store your private key in the project directly, you can do it via a `.env` file. To set up a development wallet via `.env` file, you can use the following script. ```js theme={null} // index.js import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || '')); ``` ```bash theme={null} # .env PRIVATE_KEY="" ``` To set up a development wallet via a wallet generated via [Solana CLI](https://solana.com/docs/intro/installation#solana-cli-basics), you can use the following script. ```js theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/To/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` ```js theme={null} transaction.sign([wallet]); const transactionBinary = transaction.serialize(); console.log(transactionBinary); console.log(transactionBinary.length); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "confirmed"); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); } } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); }; ``` ## Deposit and Withdraw Using the Deposit or Withdraw endpoint, the user can do so based on the `amount` of assets to be deposited/withdrawn. **USAGE STEPS** User chooses the token. User chooses the amount of assets to deposit or withdraw in the specific token mint. Post request to get the transaction. User sign and send the transaction to the network. The mint authority mints/burns the vault tokens to/from the user. ```js theme={null} const depositTransactionResponse = await ( await ( await fetch('https://lite-api.jup.ag/lend/v1/earn/deposit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ asset: mint, amount: '100000', signer: wallet.publicKey, }) }) ) ); ``` ```js theme={null} const withdrawTransactionResponse = await ( await ( await fetch('https://lite-api.jup.ag/lend/v1/earn/withdraw', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ asset: mint, amount: '100000', signer: wallet.publicKey, }) }) ) ); ``` ## Mint and Redeem Using the Mint or Redeem endpoint, the user can do so based on the number `shares` to be minted/redeemed. **USAGE STEPS** User chooses the token. User chooses the number of shares to deposit or withdraw in the specific token mint. Post request to get the transaction. User sign and send the transaction to the network. The mint authority mints/burns the vault tokens to/from the user. ```js theme={null} const mintTransactionResponse = await ( await ( await fetch('https://lite-api.jup.ag/lend/v1/earn/mint', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ asset: mint, signer: wallet.publicKey, shares: '100000', }) }) ) ); ``` ```js theme={null} const redeemTransactionResponse = await ( await ( await fetch('https://lite-api.jup.ag/lend/v1/earn/redeem', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ asset: mint, signer: wallet.publicKey, shares: '100000', }) }) ) ); ``` ## Build Your Own Transaction The Lend API provides 2 ways to interface with the Earn functions in the Jupiter Lend Program. You can either make a post request to directly get the **Transaction**, or **Instruction** which can be used for CPI or composing with additional instructions. ### Transaction To use the Transaction method, simply request to the endpoints without `-instructions` suffix directly, as shown in the examples above. The API will respond with an unsigned base64 transaction for the signer to sign, then sent to the network for execution. ### Instruction In some use cases, you'd prefer to utilize the instructions instead of the serialized transaction, so you can utilize with CPI or compose with other instructions. You can make a post request to `-instructions`endpoints instead. Example code snippet of using `/deposit-instructions` endpoint and building a transaction with the instructions. ```js theme={null} import { Connection, Keypair, PublicKey, TransactionMessage, TransactionInstruction, VersionedTransaction } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/private/key', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const connection = new Connection('insert-your-own-rpc'); const depositIx = await ( await fetch ( 'https://lite-api.jup.ag/lend/v1/earn/deposit-instructions', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ asset: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', amount: '1000000', signer: wallet.publicKey, }, null, 2) } ) ).json(); console.log(JSON.stringify(depositIx, null, 2)); const deserializeInstruction = (instruction) => { return new TransactionInstruction({ programId: new PublicKey(instruction.programId), keys: instruction.accounts.map((key) => ({ pubkey: new PublicKey(key.pubkey), isSigner: key.isSigner, isWritable: key.isWritable, })), data: Buffer.from(instruction.data, 'base64'), }); }; const blockhash = (await connection.getLatestBlockhash()).blockhash; const messageV0 = new TransactionMessage({ payerKey: wallet.publicKey, recentBlockhash: blockhash, instructions: [ ...depositIx.instructions.map(deserializeInstruction) ], }).compileToV0Message(); const transaction = new VersionedTransaction(messageV0); transaction.sign([wallet]); const transactionBinary = transaction.serialize(); console.log(transactionBinary); console.log(transactionBinary.length); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "confirmed"); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); } } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); }; ``` ### CPI * Refer to [https://github.com/jup-ag/jupiter-lend/blob/main/docs/earn/cpi.md](https://github.com/jup-ag/jupiter-lend/blob/main/docs/earn/cpi.md) for CPI example * Refer to [https://github.com/jup-ag/jupiter-lend/blob/main/target/idl/lending.json](https://github.com/jup-ag/jupiter-lend/blob/main/target/idl/lending.json) for IDL ## Tokens Jupiter Lend provides Earnings for individual tokens, meaning SOL and USDC will be deposited in isolation. To get all token information such as the underlying token, supply, rates and liquidity information. ```js theme={null} const vaults = await ( await fetch ( 'https://lite-api.jup.ag/lend/v1/earn/tokens' ) ).json(); ``` ## User Data Below are the endpoints to aid user to better manage their positions with data of each existing positions, earnings, etc. ### Positions Given a user, you are able to get their existing position data such as shares, underlying assets, balance and allowance. ```js theme={null} const userPositions = await ( await fetch ( 'https://lite-api.jup.ag/lend/v1/earn/positions?users={user1},{user2}' ) ).json(); ``` ### Earnings Given a user, you are able to get the rewards of a specific position, for example, the amount earned for USDC token position. ```js theme={null} const userRwards = await ( await fetch ( 'https://lite-api.jup.ag/lend/v1/earn/earnings?user={user1}&positions={position1},{position2}' ) ).json(); ``` # About Lend Source: https://dev.jup.ag/docs/lend/index Overview of Jupiter Lend and its features. The Jupiter Lend API is built on top of Jupiter Lend Program. ## About Earn The Earn Protocol is the 'Deposit and Earn' side of Jupiter Lend. Simply deposit assets to the Jupiter Earn and earn yield. Jupiter Lend uses a unified liquidity layer where both Earn (lending) and Borrow (vault) protocol can source liquidity from. For depositors this means you earn the best possible rate at all times without having to migrate your funds when new protocols are launched on Jupiter Lend. You can supply once and earn the most up to date yield from the Jupiter Lend protocol. There is no limits on supplying funds to the Earn Protocol. Withdrawals from Jupiter Lend utilize an Automated Debt Ceiling. Withdrawals increase every block creating a smoothing curve for withdrawals preventing any sudden large movements. Jupiter Lend is a novel protocol and like all DeFi protocols contains smart contract risk, market risk and other factors which can cause loss of user funds. There are no fees to use the Earn Protocol on Jupiter Lend. ## About Borrow Borrow Vaults are a known standard mechanism for locking collateral and borrowing debt. Jupiter Lend utilizes this familiar single asset - single debt vault approach. Jupiter Lend takes borrow vaults to the next level by being the most capital efficient and optimized protocol enabling up to 95% LTV on collateral. Jupiter borrow vaults has the most advanced liquidation mechanisms, and are able to provide the highest LTVs in the market, the protocol easily removes bad debt and enables the most gas efficient liquidation mechanism in DeFi. When your NFT or position is liquidated, a portion of your collateral is sold to repay your debt and return your position to a safe state. In addition to selling a part of your collateral, a liquidation penalty is also charged. While the Liquidation Threshold determines when a vault can be liquidated, the protocol also has a 'hard' ceiling for liquidation. When a vault passes the max liquidation threshold it is entirely (100%) liquidated automatically. **Yes your position is still at risk of being liquidated!** Once your position passes the threshold it can be liquidated, but it may not happen immediately. If your position is still at risk you can take the time now to unwind/reduce your risk ratio to make your position safe and prevent a liquidation event. ## Program ID | Program | Address | | :----------------------------- | :---------------------------------------------------------------------------------------------------------------------- | | **Jupiter Lend Earn** | [`jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9`](https://solscan.io/account/jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9) | | **Jupiter Lend Borrow** | [`jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi`](https://solscan.io/account/jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi) | | **Jupiter Lend Earn Rewards** | [`jup7TthsMgcR9Y3L277b8Eo9uboVSmu1utkuXHNUKar`](https://solscan.io/account/jup7TthsMgcR9Y3L277b8Eo9uboVSmu1utkuXHNUKar) | | **Jupiter Lend Liquidity** | [`jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC`](https://solscan.io/account/jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC) | | **Jupiter Lend Borrow Oracle** | [`jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc`](https://solscan.io/account/jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc) | # Liquidation Source: https://dev.jup.ag/docs/lend/liquidation Walkthrough on building a liquidation bot with Jupiter Lend. Jupiter Lend allows anyone to participate in the liquidation mechanism. In this section, we have included a minimal Typescript example to get you started. ## Prerequisites The liquidation bot requires the Jupiter Lend SDK. [You can find the full example at the end of this section](#full-example). We will be using functions from `@jup-ag/lend/borrow` and `/flashloan`. ```bash theme={null} npm install @jup-ag/lend ``` You will also need reliable RPC to fetch information like liquidations or swap quote and to execute the liquidation. ## Breakdown The liquidation bot requires the following steps: 1. Fetch all available liquidations 2. Flash borrow the debt amount 3. Get liquidation instructions 4. Get Jupiter swap quote (collateral token -> debt token) 5. Get Jupiter swap instructions 6. Build all instructions together 7. Execute the liquidation 8. Flash payback the debt amount ### Fetch liquidations By using the SDK, you can fetch for all available liquidations. ```typescript theme={null} const fetchAllLiquidations = await getAllLiquidations({ connection, signer, }); ``` **RPC Rate Limit** The `getAllLiquidations` function gives you the available liquidations across all vault, each request is processed in parallel, and each consumes \~10 RPC requests. If you receive **`429`** or rate limit error code from this function, it is likely due to your RPC connection getting rate limited. You should upgrade your RPC plan to avoid this issue. **Fetch liquidations for specific vault** If you'd like to fetch liquidations for a specific vault, you can use the `getLiquidations` function. ```typescript theme={null} const fetchLiquidationsByVaultId = await getLiquidations({ vaultId, connection, signer, }); ``` ### Flash borrow and payback By using the SDK, you can flashloan to borrow the debt amount and flash payback what you flash borrowed. ```typescript theme={null} const fetchFlashBorrowIx = await getFlashBorrowIx({ amount: debtAmount, asset: borrowToken, signer, connection, }); ``` ```typescript theme={null} const fetchFlashPaybackIx = await getFlashPaybackIx({ amount: debtAmount, asset: borrowToken, signer, connection, }); ``` **ALL flashloans are free**! ### Get liquidation instructions By using the SDK, you can get the liquidation instructions. ```typescript theme={null} const fetchLiquidateIx = await getLiquidateIx({ vaultId, debtAmount, signer, connection, }); ``` ### Get Jupiter quote and swap instructions For this step, you will need to request to the Jupiter Swap API to get the quote and swap instructions. * [You can refer to the example below to check how to fetch from the Swap API](#full-example) * Else, [for the full guide on Swap API, please refer to Swap API section](/docs/swap). ## Full Example ```typescript theme={null} import { getLiquidateIx, getAllLiquidations, getVaultsProgram, } from "@jup-ag/lend/borrow"; import { getFlashBorrowIx, getFlashPaybackIx } from "@jup-ag/lend/flashloan"; import { Connection, PublicKey, ComputeBudgetProgram, TransactionMessage, VersionedTransaction, TransactionInstruction, AddressLookupTableAccount, } from "@solana/web3.js"; import BN from "bn.js"; import axios from "axios"; const RPC_URL = ""; const SLIPPAGE_BPS = 100; const signer = new PublicKey("1234567890"); const connection = new Connection(RPC_URL); const program = getVaultsProgram({ connection, signer }); const configs = await program.account.vaultConfig.all(); async function fetchLiquidations() { try { const allAvailableLiquidations = await getAllLiquidations({ connection, signer, }); const validLiquidations: any = []; for (const vaultLiquidation of allAvailableLiquidations) { const { liquidations, vaultId } = vaultLiquidation; if (liquidations.length === 0) continue; // prettier-ignore for (const liquidation of liquidations) { const supplyToken = configs.find((config) => config.account.vaultId === vaultId)?.account.supplyToken; const borrowToken = configs.find((config) => config.account.vaultId === vaultId)?.account.borrowToken; validLiquidations.push({ vaultId, liquidation, debtAmount: new BN(liquidation.amtIn), collateralAmount: new BN(liquidation.amtOut), supplyToken, borrowToken, }); } } return validLiquidations; } catch (error) { console.error("Error fetching liquidations:", error); throw error; } } async function getJupiterQuote({ inputMint, outputMint, amount, slippageBps = SLIPPAGE_BPS, }) { try { const response = await axios.get("https://lite-api.jup.ag/swap/v1/quote", { params: { inputMint, outputMint, amount, slippageBps, restrictIntermediateTokens: true, maxAccounts: 32, }, }); return response.data; } catch (error) { console.error( "Error fetching Jupiter quote:", error.response?.data || error.message ); throw error; } } async function getJupiterSwapInstructions({ quoteResponse, userPublicKey }) { try { const response = await axios.post( "https://lite-api.jup.ag/swap/v1/swap-instructions", { quoteResponse, userPublicKey, }, { headers: { "Content-Type": "application/json" }, } ); if (response.data.error) { throw new Error( "Failed to get swap instructions: " + response.data.error ); } return response.data; } catch (error) { console.error( "Error getting swap instructions:", error.response?.data || error.message ); throw error; } } function deserializeInstruction(instruction) { return new TransactionInstruction({ programId: new PublicKey(instruction.programId), keys: instruction.accounts.map((key) => ({ pubkey: new PublicKey(key.pubkey), isSigner: key.isSigner, isWritable: key.isWritable, })), data: Buffer.from(instruction.data, "base64"), }); } async function getAddressLookupTableAccounts(connection, keys) { if (!keys || keys.length === 0) return []; const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo( keys.map((key) => new PublicKey(key)) ); return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => { const addressLookupTableAddress = keys[index]; if (accountInfo) { const addressLookupTableAccount = new AddressLookupTableAccount({ key: new PublicKey(addressLookupTableAddress), state: AddressLookupTableAccount.deserialize(accountInfo.data), }); acc.push(addressLookupTableAccount); } return acc; }, []); } async function executeLiquidation({ vaultId, debtAmount, collateralAmount, supplyToken, borrowToken, }) { try { console.log(`Executing liquidation for vault ${vaultId}...`); const instructions: TransactionInstruction[] = []; let allAddressLookupTableAccounts: AddressLookupTableAccount[] = []; // Step 1: Flash borrow the debt amount const flashBorrowIx = await getFlashBorrowIx({ amount: debtAmount, asset: borrowToken, signer, connection, }); instructions.push(flashBorrowIx); // Step 2: Get liquidation instructions const { ixs: liquidateIxs, addressLookupTableAccounts: liquidateLookupTables, } = await getLiquidateIx({ vaultId, debtAmount, signer, connection, }); instructions.push(...liquidateIxs); if (liquidateLookupTables && liquidateLookupTables.length > 0) { allAddressLookupTableAccounts.push(...liquidateLookupTables); } // Step 3: Get Jupiter swap quote (collateral token -> debt token) const quoteResponse = await getJupiterQuote({ inputMint: supplyToken.toString(), // Collateral token outputMint: borrowToken.toString(), // Debt token amount: collateralAmount.toString(), slippageBps: SLIPPAGE_BPS, }); // Step 4: Get Jupiter swap instructions const swapInstructions = await getJupiterSwapInstructions({ quoteResponse, userPublicKey: signer.toString(), }); const { setupInstructions, swapInstruction: swapInstructionPayload, cleanupInstruction, addressLookupTableAddresses, } = swapInstructions; if (setupInstructions && setupInstructions.length > 0) { instructions.push(...setupInstructions.map(deserializeInstruction)); } instructions.push(deserializeInstruction(swapInstructionPayload)); if (cleanupInstruction) { instructions.push(deserializeInstruction(cleanupInstruction)); } // Step 5: Flash payback const flashPaybackIx = await getFlashPaybackIx({ amount: debtAmount, asset: borrowToken, signer, connection, }); instructions.push(flashPaybackIx); // Step 6: Get Jupiter address lookup tables if (addressLookupTableAddresses && addressLookupTableAddresses.length > 0) { const jupiterLookupTables = await getAddressLookupTableAccounts( connection, addressLookupTableAddresses ); allAddressLookupTableAccounts.push(...jupiterLookupTables); } // Step 7: Build and send transaction const latestBlockhash = await connection.getLatestBlockhash(); const messageV0 = new TransactionMessage({ payerKey: signer, recentBlockhash: latestBlockhash.blockhash, instructions: [ ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000, }), ...instructions, ], }).compileToV0Message(allAddressLookupTableAccounts); const transaction = new VersionedTransaction(messageV0); return transaction; } catch (error) { console.error(`Error executing liquidation for vault ${vaultId}:`, error); throw error; } } async function runLiquidationBot() { try { const liquidations = await fetchLiquidations(); if (liquidations.length === 0) { console.log("No liquidations available at this time."); return; } for (const liquidationData of liquidations) { const { vaultId, debtAmount, collateralAmount, borrowToken, supplyToken, } = liquidationData; try { const signature = await executeLiquidation({ vaultId, debtAmount, collateralAmount, borrowToken, supplyToken, }); console.log(`Successfully liquidated vault ${vaultId}: ${signature}`); await new Promise((resolve) => setTimeout(resolve, 2000)); } catch (error) { console.error(`Failed to liquidate vault ${vaultId}:`, error.message); continue; } } } catch (error) { console.error("Error in liquidation bot:", error); } } ``` # Oracles Source: https://dev.jup.ag/docs/lend/oracles Overview of Jupiter Lend Oracles. The Oracle Program delivers accurate price data to the protocol by integrating with trusted oracle providers. It calculates exchange rates by combining multiple price sources in a structured sequence, ensuring reliable asset valuations. **NOTE** The Oracle program has been audited by Zenith and Offside. **Oracle Program Address:** `jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc` ## Hop-Based Oracle System The system computes exchange rates by processing prices from up to four sources in a sequential chain. Each source contributes to the final rate through multiplication or division, with the option to invert values as needed. Currently, Pyth is the supported source type. For example, to derive the JUPSOL/SOL rate, the system combines JUPSOL/USD and SOL/USD feeds, inverting the latter to obtain USD/SOL, resulting in an accurate exchange rate. This design enables the system to: * Aggregate rates from multiple feeds, reducing dependency on any single provider * Adjust for varying units or scales using predefined multipliers and divisors * Validate data integrity at each step ## Freshness Enforcement To ensure prices reflect current market conditions, the system enforces strict time-based validity checks: * **User Operations**: Prices must be no older than 60 seconds to be considered valid for actions like borrowing or supplying assets * **Liquidations**: Prices can be up to 1800 seconds (30 minutes) old, allowing liquidations to proceed during temporary oracle delays while avoiding reliance on outdated data These checks prevent the protocol from using stale prices for liquidation or health factor calculations. ## Confidence Interval Validation The system evaluates the confidence interval provided by Pyth price feeds to ensure data reliability: * **User Operations**: The confidence interval must be within 2% of the reported price * **Liquidations**: The confidence interval must be within 4% of the reported price If a price feed's confidence exceeds these thresholds, it is rejected. ## Providers The program currently utilizes Pyth push feeds for price data. Integration with Chainlink streams is underway to broaden the range of sources. ## Oracle Verification Script Use this script to verify oracle prices by providing a nonce: ```typescript theme={null} import { Program, AnchorProvider } from "@coral-xyz/anchor"; import { Connection, PublicKey } from "@solana/web3.js"; import axios from "axios"; const NONCE = 2; // Change this to test different oracles const ORACLE_PROGRAM_ID = new PublicKey("jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc"); const IDL_URL = "https://raw.githubusercontent.com/jup-ag/jupiter-lend/refs/heads/main/target/idl/oracle.json"; const RPC_URL = ""; class OracleReader { private program: Program; private constructor(program: Program) { this.program = program; } static async create(): Promise { const connection = new Connection(RPC_URL); const response = await axios.get(IDL_URL); const idl = response.data; const wallet = { signTransaction: () => { throw new Error("Read-only"); }, signAllTransactions: () => { throw new Error("Read-only"); }, publicKey: new PublicKey("11111111111111111111111111111111"), }; const provider = new AnchorProvider(connection, wallet, {}); const program = new Program(idl, provider); return new OracleReader(program); } private findOraclePDA(nonce: number): PublicKey { const [pda] = PublicKey.findProgramAddressSync( [ Buffer.from("oracle"), Buffer.from(new Uint8Array(new Uint16Array([nonce]).buffer)), ], ORACLE_PROGRAM_ID ); return pda; } async getPrice(nonce: number) { const oraclePDA = this.findOraclePDA(nonce); const oracleAccount = await this.program.account.oracle.fetch(oraclePDA); const remainingAccounts = oracleAccount.sources.map((source: any) => ({ pubkey: source.source, isWritable: false, isSigner: false, })); const price = await this.program.methods .getExchangeRateOperate(nonce) .accounts({ oracle: oraclePDA }) .remainingAccounts(remainingAccounts) .view(); return { nonce, oraclePDA: oraclePDA.toString(), price: price.toString(), sources: oracleAccount.sources.length }; } } async function main() { const reader = await OracleReader.create(); const result = await reader.getPrice(NONCE); console.log(result); } main().catch(console.error); ``` # Lend SDK Source: https://dev.jup.ag/docs/lend/sdk Walkthrough on integrating via the Jupiter Lend SDK. The Jupiter Lend SDK provides a TypeScript interface for interacting with the Jupiter lending protocol. This documentation covers two main integration approaches: getting instruction objects for direct use and getting account contexts for Cross-Program Invocation (CPI) integrations. ## Installation ```bash theme={null} npm install @jup-ag/lend ``` ## Setup ```typescript theme={null} import { Connection, Keypair, PublicKey, TransactionMessage, TransactionInstruction, VersionedTransaction } from "@solana/web3.js"; import { getDepositIx, getWithdrawIx, // get instructions getDepositContext, getWithdrawContext, // get context accounts for CPI } from "@jup-ag/lend/earn"; import { BN } from "bn.js"; const connection = new Connection("https://api.mainnet-beta.solana.com"); const signer = Keypair.fromSecretKey(new Uint8Array(privateKey)); // Example asset mints const usdc = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC mainnet ``` *** ## Instruction ### Get Deposit Instruction ```typescript theme={null} const depositIx = await getDepositIx({ amount: new BN(1000000), // amount in token decimals (1 USDC) asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // asset mint address signer: signer.publicKey, // signer public key connection, // Solana connection cluster: "mainnet", }); ``` ### Get Withdraw Instruction ```typescript theme={null} const withdrawIx = await getWithdrawIx({ amount: new BN(1000000), // amount in token decimals (1 USDC) asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // asset mint address signer: signer.publicKey, // signer public key connection, // Solana connection cluster: "mainnet", }); ``` ### Example Instruction Usage ```typescript theme={null} import { Connection, Keypair, PublicKey, TransactionMessage, Transaction, TransactionInstruction, VersionedTransaction } from "@solana/web3.js"; import { getDepositIx, } from "@jup-ag/lend/earn"; import { BN } from "bn.js"; const signer = Keypair.fromSecretKey(new Uint8Array(privateKey)); const connection = new Connection('https://api.mainnet-beta.solana.com'); // Get deposit instruction const depositIx = await getDepositIx({ amount: new BN(1000000), // amount in token decimals (1 USDC) asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // asset mint address signer: signer.publicKey, // signer public key connection, // Solana connection cluster: "mainnet", }); // Convert the raw instruction to TransactionInstruction const instruction = new TransactionInstruction({ programId: new PublicKey(depositIx.programId), keys: depositIx.keys.map((key) => ({ pubkey: new PublicKey(key.pubkey), isSigner: key.isSigner, isWritable: key.isWritable, })), data: Buffer.from(depositIx.data), }); const latestBlockhash = await connection.getLatestBlockhash(); const messageV0 = new TransactionMessage({ payerKey: signer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: [instruction], }).compileToV0Message(); const transaction = new VersionedTransaction(messageV0); transaction.sign([signer]); const serializedTransaction = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" }); const signature = await connection.sendRawTransaction(serializedTransaction); console.log(`https://solscan.io/tx/${signature}`); ``` ## CPI For Anchor programs that need to make CPI calls to Jupiter Lend, use the context methods. ### Deposit Context Accounts ```typescript theme={null} const depositContext = await getDepositContext({ asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // asset mint address signer: signer.publicKey, // signer public key connection, }); ```
Deposit Context Accounts Table
| Account | Purpose | | :--------------------------------- | :--------------------------------------- | | `signer` | User's wallet public key | | `depositorTokenAccount` | User's underlying token account (source) | | `recipientTokenAccount` | User's fToken account (destination) | | `mint` | Underlying token mint | | `lendingAdmin` | Protocol configuration PDA | | `lending` | Pool-specific configuration PDA | | `fTokenMint` | fToken mint account | | `supplyTokenReservesLiquidity` | Liquidity protocol token reserves | | `lendingSupplyPositionOnLiquidity` | Protocol's position in liquidity pool | | `rateModel` | Interest rate calculation model | | `vault` | Protocol vault holding deposited tokens | | `liquidity` | Main liquidity protocol PDA | | `liquidityProgram` | Liquidity protocol program ID | | `rewardsRateModel` | Rewards calculation model PDA |
### Withdraw Context Accounts ```typescript theme={null} const withdrawContext = await getWithdrawContext({ asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // asset mint address signer: signer.publicKey, // signer public key connection, }); ```
Withdraw Context Accounts Table
Similar to deposit context, but includes: * `ownerTokenAccount`: User's fToken account (source of fTokens to burn) * `claimAccount`: Additional account for withdrawal claim processing | Account | Purpose | | :--------------------------------- | :--------------------------------------- | | `signer` | User's wallet public key | | `ownerTokenAccount` | User's underlying token account (source) | | `recipientTokenAccount` | User's fToken account (destination) | | `claimAccount` | Additional account for withdrawal | | `mint` | Underlying token mint | | `lendingAdmin` | Protocol configuration PDA | | `lending` | Pool-specific configuration PDA | | `fTokenMint` | fToken mint account | | `supplyTokenReservesLiquidity` | Liquidity protocol token reserves | | `lendingSupplyPositionOnLiquidity` | Protocol's position in liquidity pool | | `rateModel` | Interest rate calculation model | | `vault` | Protocol vault holding deposited tokens | | `liquidity` | Main liquidity protocol PDA | | `liquidityProgram` | Liquidity protocol program ID | | `rewardsRateModel` | Rewards calculation model PDA |
### Example CPI Usage ```typescript theme={null} const depositContext = await getDepositContext({ asset: usdcMint, signer: userPublicKey, }); // Pass these accounts to your Anchor program await program.methods .yourDepositMethod(amount) .accounts({ // Your program accounts userAccount: userAccount, // Jupiter Lend accounts (from context) signer: depositContext.signer, depositorTokenAccount: depositContext.depositorTokenAccount, recipientTokenAccount: depositContext.recipientTokenAccount, lendingAdmin: depositContext.lendingAdmin, lending: depositContext.lending, fTokenMint: depositContext.fTokenMint, // ... all other accounts from context lendingProgram: new PublicKey( "jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9" ), }) .rpc(); ``` *** ## Read Functions The Jupiter Lend SDK provides several read functions to query protocol data and user positions, this can be helpful to display on your frontend. ### Get All Lending Tokens Retrieves all available lending tokens in the Jupiter Lend Earn protocol. The `getLendingTokens` function returns an array of `PublicKey` objects. ```typescript theme={null} import { getLendingTokens } from "@jup-ag/lend/earn"; const allTokens = await getLendingTokens({ connection }); ``` ```typescript theme={null} [ PublicKey, PublicKey, ... ] ``` ### Get Token Details Fetches detailed information about a specific lending token. ```typescript theme={null} import { getLendingTokenDetails } from "@jup-ag/lend/earn"; const tokenDetails = await getLendingTokenDetails({ lendingToken: new PublicKey("9BEcn9aPEmhSPbPQeFGjidRiEKki46fVQDyPpSQXPA2D"), // allTokens[x] from the previous example connection, }); ``` ```typescript theme={null} { id: number; // ID of jlToken, starts from 1 address: PublicKey; // Address of jlToken asset: PublicKey; // Address of underlying asset decimals: number; // Decimals of asset (same as jlToken decimals) totalAssets: BN; // Total underlying assets in the pool totalSupply: BN; // Total shares supply convertToShares: BN; // Multiplier to convert assets to shares convertToAssets: BN; // Multiplier to convert shares to assets rewardsRate: BN; // Rewards rate (1e4 decimals, 1e4 = 100%) supplyRate: BN; // Supply APY rate (1e4 decimals, 1e4 = 100%) } ``` ### Get User Position Retrieves a user's lending position for a specific asset: ```typescript theme={null} import { getUserLendingPositionByAsset } from "@jup-ag/lend/earn"; const userPosition = await getUserLendingPositionByAsset({ asset: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // The address of underlying asset or tokenDetails.asset user: signer.publicKey, // User's wallet address connection, }); ``` ```typescript theme={null} { lendingTokenShares: BN; // User's shares in jlToken underlyingAssets: BN; // User's underlying assets underlyingBalance: BN; // User's underlying balance } ``` # Jupiter Lock Source: https://dev.jup.ag/docs/lock/index Jupiter Lock is an open-sourced, audited, and free ecosystem tool to lock and distribute tokens over-time. Jupiter Lock is an open-sourced, audited and free-to-use tool to lock and distribute tokens over-time. * Used by project teams, creators, community builders, and anyone * Implement cliff, and vest non-circulating supply in a clear and transparent manner. * Program code is available here: [https://github.com/jup-ag/jup-lock](https://github.com/jup-ag/jup-lock) * Program ID: [`LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn`](https://solscan.io/account/LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn) * Audited Twice by [OtterSec & Sec3](/resources/audits) * Lock UI: [https://lock.jup.ag/](https://lock.jup.ag/) Refer to this section of the Github repository to use Lock programmatically. # Custody Account Source: https://dev.jup.ag/docs/perps/custody-account This page contains an overview of the used in the Jupiter Perpetuals Program, and specifically the account. The `Custody` account is a struct which represents a set of parameters and states associated to custodies (tokens) managed by the JLP pool which consists of the following custodies. | Custodies | | | | | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | [SOL](https://solscan.io/account/7xS2gz2bTp3fwCC7knJvUWTEU9Tycczu6VhJYKgi1wdz) | [ETH](https://solscan.io/account/AQCGyheWPLeo6Qp9WpYS9m3Qj479t7R636N9ey1rEjEn) | [BTC](https://solscan.io/account/5Pv3gM9JrFFH883SWAhvJC9RPYmo8UNxuFtv5bMMALkm) | [USDC](https://solscan.io/account/G18jKKXQwBbrHeiK3C9MRXhkHsLHf7XgCSisykV46EZa) | [USDT](https://solscan.io/account/4vkNeXiYEUizLdrpdPS1eC2mccyM4NUPRtERrk6ZETkk) | This [repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing) contains Typescript code samples on interacting with the Jupiter Perpetuals program IDL with `anchor` and `@solana/web3.js` You can also find the [Custody Account fields in the repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing/blob/1a0b5dc71081958895691047a9aa8ba51d2a8765/src/idl/jupiter-perpetuals-idl.ts#L2397) or on a [blockchain explorer](https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu#anchorProgramIdl). ## Account Details Each `Custody` account contains the following data: | Field | Description | | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pool` | **Type:** `publicKey`

The public key for the pool that this custody belongs to (i.e. the JLP pool). | | `mint` | **Type:** `publicKey`

The public key for the custody's token mint account. | | `tokenAccount` | **Type:** `publicKey`

The associated token account of the custody which holds the tokens under management for the pool. | | `decimals` | **Type:** `u8`

The number of decimals used for the token which is the same as the number of decimals specified in the token mint account. This is stored for convenience. | | `isStable` | **Type:** `bool`

A boolean flag indicating if the token in custody is a stable asset. | | `oracle` | **Type:** `OracleParams`

Contains data for the price oracle used for the custody. | | `pricing` | **Type:** [`PricingParams`](#pricingparams)

Contains data for the custody's price-related logic. | | `permissions` | **Type:** `Permissions`

A set of global flags that can be set by the protocol's administrator to enable or disable trade actions which is useful during program upgrades or black swan events. | | `targetRatioBps` | **Type:** `u64`

The target weightage (in basis points) for the custody in the JLP pool. | | `assets` | **Type:** [`Assets`](#assets)

Contains data used to calculate PNL, AUM, and core business logic for the program. | | `fundingRateState` | **Type:** [`FundingRateState`](#fundingratestate)

Contains data used to calculate borrow fees for open positions. | ### `PricingParams` | Field | Description | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `tradeImpactFeeScalar` | **Type:** `u64`

Sets the base value when calculating price impact fees when opening or closing positions. | | `maxLeverage` | **Type:** `u64`

Sets the max leverage for this custody's positions. The max leverage for all custodies is 500x at the time of writing. | | `maxGlobalLongSizes` | **Type:** `u64`

The maximum total position size (USD) for long positions. | | `maxGlobalShortSizes` | **Type:** `u64`

The maximum total position size (USD) for short positions. | ### `Assets` | Field | Description | | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `feesReserves` | **Type:** `u64`

The fees collected by all open positions for the custody. `feesReserves` resets to zero when the fees are distributed to the pool and protocol. | | `owned` | **Type:** `u64`

The number of tokens owned by the pool for the custody.
- The owned value is increased either by providing liquidity to the pool or depositing collateral when opening or updating positions.
- Conversely, the owned value decreases when liquidity is removed from the pool or collateral is withdrawn from closing positions. | | `locked` | **Type:** `u64`

The number of tokens locked by the pool for the custody to pay off potential profits for open positions. | | `guaranteedUsd` | **Type:** `u64`

This value represents the total amount borrowed in USD (position size - collateral) across all long positions.

It is updated whenever traders modify their collateral through deposits or withdrawals. The system uses this aggregated figure to efficiently calculate the total profit and loss (PNL) for all long positions, which in turn is used to calculate the AUM of the JLP pool. | | `globalShortSizes` | **Type:** `u64`

Stores the total amount (USD) position sizes for all short positions. | | `globalShortAveragePrices` | **Type:** `u64`

Stores the average price (USD) for all short positions.

This value and `globalShortSizes` are used to calculate the PNL for all short positions efficiently, and is again used to calculate the AUM of the JLP pool. | ### `FundingRateState` | Field | Description | | :----------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cumulativeInterestRate` | **Type:** `u128`

Traders are required to pay hourly borrow fees for opening leveraged positions. This fee is calculated based on two primary factors: the size of the trader's position and the current utilization of the pool for the custody.

To calculate borrow fees more efficiently, each custody account contains a value called `cumulativeInterestRate`.

Correspondingly, each position account stores a `cumulativeInterestSnapshot` which captures the value of `cumulativeInterestRate` at the time of the position's last update. Whenever there's a change in either the borrowed assets or the total assets within a custody, the `cumulativeInterestRate` for the custody is updated. The difference between the custody's `cumulativeInterestRate` and the position's `cumulativeInterestSnapshot` is then used to calculate the position's borrow fees. | | `lastUpdate` | **Type:** `i64`

The UNIX timestamp for when the custody's borrow fee data was last updated. | | `hourlyFundingDbps` | **Type:** `u64`

A constant used to calculate the hourly borrow fees for the custody. The Jupiter Perpetuals exchange works with Gauntlet and Chaos Labs to update and fine tune the `hourlyFundingDbps` to respond to traders' feedback and market conditions. | # About Perps API Source: https://dev.jup.ag/docs/perps/index **WARNING** The Perps API is still a **work in progress**, stay tuned! **TIP** In the meantime, you can use this amazing github repository to direct Anchor IDL parse the Perps Program. * Fetch Perps or JLP pool data * Interact with the Perps Program [https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing) # Pool Account Source: https://dev.jup.ag/docs/perps/pool-account This page contains an overview of the used in the Jupiter Perpetuals Program, and specifically the account. The `Pool` account is a struct which represents a set of parameters and states associated to the data for JLP pool, including AUM and [`Custody`](/docs/perps/custody-account) data. **ONLY ONE POOL ACCOUNT** There is only one [`Pool` account](https://solscan.io/account/5BUwFW4nRbftYTDMbgxykoFWqWHPzahFSNAaaaJtVKsq). **EXAMPLE TYPESCRIPT REPOSITORY** This [repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing) contains Typescript code samples on interacting with the Jupiter Perpetuals program IDL with `anchor` and `@solana/web3.js` You can also find the [Custody Account fields in the repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing/blob/1a0b5dc71081958895691047a9aa8ba51d2a8765/src/idl/jupiter-perpetuals-idl.ts#L2699) or on a [blockchain explorer](https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu#anchorProgramIdl). ## Account Details Each `Pool` account contains the following data: | Field | Description | | :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | **Type:** `string`

The name for the account. | | `custodies` | **Type:** `publicKey`

An array containing the public keys for the custodies (tokens) managed by the JLP pool. | | `aumUsd` | **Type:** `u128`

The current AUM value (USD) for the JLP pool. The `aumUsd` value's calculation can be summarized by getting the USD value of the tokens managed by the pool minus the USD value reserved to pay off trader profits.

Refer to the [Custody account](/docs/perps/custody-account) details for more details on AUM calculation. | | `limit` | **Type:** [`Limit`](#limit)

Contains values for the pool's limits. | | `fees` | **Type:** [`Fees`](#fees)

Sets the fee amounts or percentages for the Jupiter Perpetuals exchange. | | `poolApr` | **Type:** [`PoolApr`](#poolapr)

Contains data related to the pool's APR / APY calculations. | ### `Limit` | Field | Description | | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `maxAumUsd` | **Type:** `u128`

The max AUM for the JLP pool. This acts as a max cap / ceiling as the JLP will not accept deposits when the cap is hit. | | `tokenWeightageBufferBps` | **Type:** `u128`

The token weightage buffer (in basis points) to calculate the token's maximum or minimum current weightage based on the target weightage.

Currently, `tokenWeightageBufferBps` is set to `2000` which means the the current weightage cannot be lower or higher than + / - 20% of the token's target weightage.

For example, if SOL's target weightage for the JLP pool is 50%, the current weightage cannot be less than 40% or exceed 60%. The pool will not allow deposits or withdrawals if the action causes the token to exceed its target weightage. | | `maxPositionUsd` | **Type:** `u64`

Sets the maximum position size. The current `maxPositionUsd` value is `2_500_000_000_000` which means a position's max size is \$2,500,000. | ### `Fees` | Field | Description | | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `increasePositionBps` | **Type:** `string`

A fixed fee of 6 BPS (0.06%) is charged for opening or increasing a position. | | `decreasePositionBps` | **Type:** `publicKey`

A fixed fee of 6 BPS (0.06%) is charged for closing or decreasing a position. | | `addRemoveLiquidityBps` | **Type:** `u128`

Fee charged when adding or removing liquidity to/from the pool. | | `swapBps` | **Type:** `Limit`

Swap fee for exchanging non-stablecoin tokens routed through the liquidity pool. `swap fee = swapBps ± swapTaxBps` | | `taxBps` | **Type:** `PoolApr`

Tax fee for non-stablecoins, determined based on the difference between the current and target weightage. A larger difference results in a higher tax fee, encouraging liquidity providers to rebalance the pool to the target weightage. | | `stableSwapBps` | **Type:** `Limit`

Swap fee for exchanges involving stablecoins, routed through the liquidity pool. `swap fee = stableSwapBps ± stableSwapTaxBps` | | `stableSwapTaxBps` | **Type:** `Fees`

Tax fee for stablecoin swaps. Similar to taxBps, this fee is determined by the difference between the current and target weightage. | | `protocolShareBps` | **Type:** `PoolApr`

Jupiter takes a share of 2500 BPS (25%) from the fees collected by the pool. | ### `PoolApr` | Field | Description | | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `lastUpdated` | **Type:** `i64`

The UNIX timestamp when the pool's APR data was last updated. | | `feeAprBps` | **Type:** `u64`

The pool's APR in BPS format. The APR is calculated weekly by dividing the pool's realized fees (minus the 25% collected by the protocol) by the total pool value, adjusting for the 1 week time period to annualize the rate. | | `realizedFeeUsd` | **Type:** `u64`

The fees collected by the pool so far. This fee is reinvested back into the pool and is also used to calculate the APR as mentioned above. realizedFeeUsd resets to zero when the fee is reinvested into the pool hence causing the APR value to fluctuate weekly. | # Position Account Source: https://dev.jup.ag/docs/perps/position-account This page contains an overview of the used in the Jupiter Perpetuals Program, and specifically the account. The `Position` account is a struct which represents a set of parameters and states associated to trade position data for a given token. **`Position` account derivation** The `Position` account's address is derived from the trader's wallet address / public key, the custody account, the collateral custody account, and a few other constant seeds. This means traders will always have the same Position account address for their open positions. This also means that traders only have nine positions available at one time: * Long SOL * Long wETH * Long wBTC * Short SOL (USDC as collateral) * Short SOL (USDT as collateral) * Short wETH (USDC as collateral) * Short wETH (USDT as collateral) * Short wBTC (USDC as collateral) * Short wBTC (USDT as collateral) This is an example [`Position` account](https://solscan.io/account/FBLzd5VM67MEKkoWerXu7Nu1ksbLXQvJDx63y5aeLEvt). **EXAMPLE TYPESCRIPT CODE** This [repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing) contains Typescript code samples on interacting with the Jupiter Perpetuals program IDL with `anchor` and `@solana/web3.js` You can also find the [Custody Account fields in the repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing/blob/1a0b5dc71081958895691047a9aa8ba51d2a8765/src/idl/jupiter-perpetuals-idl.ts#L2699) or on a [blockchain explorer](https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu#anchorProgramIdl). ## Account Details Each `Position` account contains the following data: | Field | Description | | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `owner` | **Type:** `publicKey`

The public key of the trader's account. | | `pool` | **Type:** `publicKey`

The public key of the [JLP pool account](/docs/perps/pool-account). | | `custody` | **Type:** `publicKey`

The public key of the position's [`custody` account](/docs/perps/custody-account). | | `collateralCustody` | **Type:** `publicKey`

The public key of the position's collateral custody account.

Like the `custody` account, a `collateralCustody` account contains information for the token that's used as collateral for the position (SOL / wETH / wBTC for long positions, USDC / USDT for short positions).

The borrow rates for the position will also be calculated based on the position's `collateralCustody`. | | `openTime` | **Type:** `i64`

The open time of the position in UNIX timestamp format. | | `updateTime` | **Type:** `i64`

The last updated time of the position in UNIX timestamp format. | | `side` | **Type:** `Side`

The position's side, either `long` or `short`. | | `price` | **Type:** `u64`

The entry price of the position when it was opened. The entry price is an integer in the atomic value (before decimals), a USDC (6 decimals) value of `158225872` is equivalent to \$158.22. | | `sizeUsd` | **Type:** `u64`

The position size after leverage in USD in the atomic value (before decimals). A position with `sizeUsd = 0` is treated as a closed position. | | `collateralUsd` | **Type:** `u64`

The position's collateral size after fees in USD in the atomic value (before decimals). | | `realisedPnlUsd` | **Type:** `i64`

The position's realized PNL when closing the position partially.

When a position is closed completely, the position's `realisedPnlUsd` will be `0` as the position is considered closed (as described in `sizeUsd`). | | `cumulativeInterestSnapshot` | **Type:** `u128`

Stores the position's interest rate snapshot when it was last updated.

- The `collateralCustody` account for the respective collateral token stores a monotonically increasing counter in `collateralCustody .fundingRateState .cumulativeInterestRate`.

- The difference between the `collateralCustody .fundingRateState .cumulativeInterestRate` and the position's `cumulativeInterestSnapshot` is used to calculate the borrow fees for the position. | | `lockedAmount` | **Type:** `u64`

The amount of tokens (SOL / wETH / wBTC for long positions, USDC / USDT for short positions) locked to pay off the position's max potential profit. It acts as a cap on the maximum potential profit of the position. This amount is locked in the collateral custody to ensure the platform has sufficient tokens to pay out profitable trades. | | `bump` | **Type:** `u8`

The bump seed used to derive the PDA for the `Position` account. | # PositionRequest Account Source: https://dev.jup.ag/docs/perps/position-request-account This page contains an overview of the used in the Jupiter Perpetuals Program, and specifically the account. The `PositionRequest` account is a struct which represents a set of parameters and states associated to a request to open or close a position, the `PositionRequest` account consists of mostly similar properties as [`Position` account](/docs/perps/position-account). **`PositionRequest` ACCOUNT DERIVATION** It is a Program-Derived Address (PDA) derived from the underlying `Position` account's address, several constant seeds, and a random integer seed which makes each `PositionRequest` account unique. The is an example [`PositionRequest` account](https://solscan.io/account/DNnX2B1oiYqKLrbLLod1guuaZA28DQwJ8HuHsgDafoQK). **`PositionRequestATA` ACCOUNT** A `PositionRequestATA` account is created for each `PositionRequest` account. The `PositionRequestATA` account is an [associated token account](https://spl.solana.com/associated-token-account) derived from the `PositionRequest` that contains the tokens from the trader's deposits or withdrawals from withdrawing collateral or closing positions. The tokens are then transferred to the position token's custody token account or returned to the trader's wallet when the `PositionRequestATA` account is closed. **TAKE PROFIT / STOP LOSS REQUESTS** `PositionRequest` accounts for non TP / SL requests are closed as soon as the request is executed or rejected. TP / SL requests are also stored onchain via `PositionRequest` accounts. However, they will only be closed when the TP / SL request is triggered and executed. Active TP / SL requests can be fetched onchain (through blockchain explorers like Solscan or SolanaFM) by searching for the `PositionRequest` address or public key associated with the TP / SL request. **EXAMPLE TYPESCRIPT REPOSITORY** This [repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing) contains Typescript code samples on interacting with the Jupiter Perpetuals program IDL with `anchor` and `@solana/web3.js` You can also find the [Custody Account fields in the repository](https://github.com/julianfssen/jupiter-perps-anchor-idl-parsing/blob/1a0b5dc71081958895691047a9aa8ba51d2a8765/src/idl/jupiter-perpetuals-idl.ts#L2583) or on a [blockchain explorer](https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu#anchorProgramIdl). ## Account Details Each `PositionRequest` account contains the following data: | Field | Description | | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `owner` | **Type:** `publicKey`

The public key of the trader's account. | | `pool` | **Type:** `publicKey`

The public key of the [JLP pool account](/docs/perps/pool-account). | | `custody` | **Type:** `publicKey`

The public key of the position's [`custody` account](/docs/perps/custody-account). | | `collateralCustody` | **Type:** `publicKey`

The public key of the position's collateral custody account.

Like the `custody` account, a `collateralCustody` account contains information for the token that's used as collateral for the position (SOL / wETH / wBTC for long positions, USDC / USDT for short positions). The borrow rates for the position will also be calculated based on the position's `collateralCustody`. | | `mint` | **Type:** `publicKey`

For opening positions and collateral deposits, mint refers to the input mint requested by the trader.

For example, if a trader opens a position by providing the initial margin with SOL, then mint will be equal to SOL's mint address. If the trader deposits collateral in USDC, then mint will be equal to USDC's mint address.

For closing positions and collateral withdrawals, mint is equal the to position collateral token's mint address. For example, if a trader closes a long SOL position, mint will be equal to SOL's mint address. If a trader closes a short SOL position, mint is equal to USDC or USDT's mint address depending on the position's collateral. | | `openTime` | **Type:** `i64`

The time when the request of position is created in UNIX timestamp format. | | `updateTime` | **Type:** `i64`

The time when the request of position is last updated in UNIX timestamp format. | | `sizeUsdDelta` | **Type:** `u64`

The USD amount to increase or decrease the position size by. The amount is an integer in the atomic value (before decimals which is 6 for USDC / UST mints).

For example, a position request to increase an open position's size by 10 USDC will have a `sizeUsdDelta = 10000000`. | | `collateralDelta` | **Type:** `u64`

For opening positions and collateral deposits, `collateralDelta` is the token amount to increase or decrease the position collateral size by. The token amount is represented in atomic values (before decimals). | | `requestChange` | **Type:** `RequestChange`

`requestChange` will be equal to `Increase` for open position and collateral deposit requests, and `Decrease` for close position and collateral withdrawal requests. | | `requestType` | **Type:** `RequestType`

`Market` for all position requests except for TP / SL requests, which have a different `requestType` known as `Trigger`. | | `side` | **Type:** `Side`

`Long` for long positions, `Short` for short positions | | `priceSlippage` | **Type:** `u64`

The maximum price with slippage for position requests when opening, closing, or updating the position size.

- When increasing the size of a long position or decreasing the size of a short position, the request will fail if the current price of the position's token is greater than `priceSlippage`.

- When decreasing the size of a long position or increasing the size of a short position, the request will fail if `priceSlippage` is greater than the current price of the position's token. | | `jupiterMinimumOut` | **Type:** `u64`

For requests that require token swaps, the output amount of the token swap must be greater than or equal to `jupiterMinimumOut`, else the request will fail. | | `preSwapAmount` | **Type:** `u64`

This is an internal attribute used by the program to calculate the `collateralDelta` for position requests that require token swaps. | | `triggerPrice` | **Type:** `u64`

The price (USD) used for TP / SL position requests. | | `triggerAboveThreshold` | **Type:** `bool`

When `triggerAboveThreshold` is true, the TP / SL position request will be triggered when the position's token price is greater than or equal to `triggerPrice`. When `triggerAboveThreshold` is false, the TP / SL position request will be triggered when the position's token price is less than or equal to `triggerPrice`. | | `entirePosition` | **Type:** `bool`

This attribute is only checked when closing or decreasing position sizes. When `entirePosition` is true, the entire position will be closed (i.e. a close position request). When `entirePosition` is false, the position size will be reduced according to sizeUsdDelta. | | `executed` | **Type:** `bool`

Determines whether the position request is executed or not. | | `counter` | **Type:** `u64`

The random integer seed used to derive the position request address. | | `bump` | **Type:** `u8`

The bump seed used to derive the position request address. | # About Price Source: https://dev.jup.ag/docs/price/index The Jupiter Price API aims to be the source of truth of token prices across all Jupiter UIs and integrator platforms, providing a seamless experience for developers and a reliable and accurate price source for users. **DEPRECATED** [Price API V2](/docs/price/v2) will be/is deprecated by 1 August 2025. Please migrate to [Price API V3](/docs/price/v3) which consists of breaking changes. ## Challenges Accurately pricing tokens on-chain is deceptively complex. Unlike traditional markets with centralized pricing mechanisms and consistent liquidity, decentralized finance (DeFi) presents a set of dynamic and often adversarial conditions. The Price API V3 is built with these realities in mind, abstracting away challenges to deliver accurate, real-time token prices with integrity and consistency. | Challenge | Description | | :------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Gamification of Price** | In decentralized environments, token prices can be manipulated or "gamed" for appearances or exploitative purposes. Common patterns include:
\* Wash trading to inflate volume or imply activity
\* Circular swaps to fabricate higher valuations | | **Fragmented, Volatile or Imbalanced Liquidity Across Venues** | Liquidity on Solana (and other chains) is spread across numerous protocols and AMMs. No single source can represent the entire market. Different pools might have wildly different pricing and can change very quickly. | | **Low Liquidity Tokens** | Some tokens trade rarely or only within shallow pools. In such cases, even small orders can cause large price swings, making pricing unreliable. | ## How Price is Derived The latest version of Price API is V3 - which uses the **last swapped price (across all transactions)**. The swaps are priced by working outwards from a small set of reliable tokens (like SOL) whose price we get from external oracle sources. While and also after deriving the last swap price, we also utilize a number of heuristics to ensure the accuracy of the price and eliminate any outliers: * Asset origin and launch method * Market liquidity metrics * Market behaviour patterns * Holder distribution statistics * Trading activity indicators * Market value to liquidity ratios :::caution When using Price API, do note that you may face many tokens where price is not available or returns null. This is because, we use the aforementioned heuristics to determine the price of a token and if the price is reliable - if certain combinations of these factors indicate potential issues with price reliability or market health, the token will be flagged and not provided a price. This is to safeguard users and prevent an inaccurate price from being returned. ::: # Price API V2 (Deprecated) Source: https://dev.jup.ag/docs/price/v2 Price API V2 aims to enhance accuracy by incorporating both **buy** and **sell-side liquidity** to derive the **average price** of the two. **DEPRECATED** This version of the Price API V2 will be/is deprecated by 30th September 2025. Please migrate to [Price API V3](/docs/price/v3) which consists of breaking changes. This provides more reliable real-time data for SPL tokens. Additionally, V2 provides extra help information like depth and confidence to aid you or your users with decisions. **TIP** The prices are derived **from Jupiter Swap**, which is an aggregate of most markets on Solana. ## Let’s Get Started In this guide, we will be going through the simple price responses and the extra help information. ## Get Price (Only Price) Using the root URL and parameters to pass in, it is as simple as the example code below! Notice the `ids` parameter with the public key or token address of a token mint, you can also input more than 1 address by comma-separating them. #### Price vs USDC by default ```js theme={null} const priceResponse = await fetch( 'https://lite-api.jup.ag/price/v2?ids=JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN,So11111111111111111111111111111111111111112' ); const priceData = await priceResponse.json(); console.log(priceData); ``` #### Price vsToken ```js theme={null} console.log(JSON.stringify(priceData, null, 2)); const priceResponseWithVsToken = await fetch( 'https://lite-api.jup.ag/price/v2?ids=JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN,So11111111111111111111111111111111111111112&vsToken=So11111111111111111111111111111111111111112' ); const priceDataWithVsToken = await priceResponseWithVsToken.json(); console.log(JSON.stringify(priceDataWithVsToken, null, 2)); ``` From the above example, you should see this response. Notice 2 details here: * Usage of `vsToken`: The first set of data shows price denoted in USDC while the second set of data denotes in the price of SOL. * With no `showExtraInfo`: There is only 1 price, the derived price is the buy price. ```json expandable theme={null} { "data": { "So11111111111111111111111111111111111111112": { "id": "So11111111111111111111111111111111111111112", "type": "derivedPrice", "price": "210.195311500" }, "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": { "id": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", "type": "derivedPrice", "price": "1.084247" } }, "timeTaken": 0.00488491 } { "data": { "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": { "id": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", "type": "derivedPrice", "price": "0.005158283466279884" }, "So11111111111111111111111111111111111111112": { "id": "So11111111111111111111111111111111111111112", "type": "derivedPrice", "price": "1" } }, "timeTaken": 0.00203215 } ``` ## Get Price (with Extra Info) To get extra help information such as confidence level or depth, you will need to pass in `showExtraInfo=true`. However, do note that if this is set to `true`, you will not be able to apply `vsToken`. ```js theme={null} const priceResponseShowExtraInfo = await fetch( 'https://lite-api.jup.ag/price/v2?ids=JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN,So11111111111111111111111111111111111111112&showExtraInfo=true' ); const priceDataShowExtraInfo = await priceResponseShowExtraInfo.json(); console.log(JSON.stringify(priceDataShowExtraInfo, null, 2)); ``` Here is the sample response. Notice a few details here: * You can see both last swap and current quote prices. * You can see both buy and sell prices of the different types. * You can see the unix timestamps. * You can see the confidence and depth information. ```json expandable theme={null} { "data": { "So11111111111111111111111111111111111111112": { "id": "So11111111111111111111111111111111111111112", "type": "derivedPrice", "price": "210.734462500", "extraInfo": { "lastSwappedPrice": { "lastJupiterSellAt": 1731599242, "lastJupiterSellPrice": "210.52136418853988", "lastJupiterBuyAt": 1731599242, "lastJupiterBuyPrice": "210.5553945976539" }, "quotedPrice": { "buyPrice": "210.578367000", "buyAt": 1731599236, "sellPrice": "210.890558000", "sellAt": 1731599236 }, "confidenceLevel": "high", "depth": { "buyPriceImpactRatio": { "depth": { "10": 0.08186978526745424, "100": 0.1154072102743595, "1000": 0.13766677800178445 }, "timestamp": 1731599207 }, "sellPriceImpactRatio": { "depth": { "10": 0.1211367007033883, "100": 0.059088081285986374, "1000": 0.16445602954342006 }, "timestamp": 1731599207 } } } }, "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": { "id": "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", "type": "derivedPrice", "price": "1.088080", "extraInfo": { "lastSwappedPrice": { "lastJupiterSellAt": 1731599239, "lastJupiterSellPrice": "1.0857748923629837", "lastJupiterBuyAt": 1731599241, "lastJupiterBuyPrice": "1.0879206578017573" }, "quotedPrice": { "buyPrice": "1.088085", "buyAt": 1731599236, "sellPrice": "1.088076", "sellAt": 1731599236 }, "confidenceLevel": "high", "depth": { "buyPriceImpactRatio": { "depth": { "10": 0.05662764967204097, "100": 0.17463135504551536, "1000": 0.7379832960897882 }, "timestamp": 1731599207 }, "sellPriceImpactRatio": { "depth": { "10": 0.03504801758790863, "100": 0.16858843747627028, "1000": 3.0578377037958586 }, "timestamp": 1731599207 } } } } }, "timeTaken": 0.003665979 } ``` ## Limitations **Query limits** 1. You can query up to 100 `id`s at once. **If the price for a token cannot be found, it is either because** 1. The token is not tradable on Jupiter - it does not fit Jupiter’s routing criteria. 2. There is no route for this token to SOL. 3. `sellPrice`, `sellAt` & `lastSwappedPrice` might be null in cases 4. If `sellPrice` & `sellAt` is not cached and cannot be retrieved the provided information will be `buyPrice`. 5. `lastSwappedPrice` might be null if the token has not been traded recently or cannot be retrieved. 6. Tokens that have not been traded via USDC in the last 3 days. 7. Note that this is only for swaps done via Jupiter, it will not be done for swaps done e.g. directly on Raydium’s platform **`buyPriceImpactRatio` & `sellPriceImpactRatio` in the depth field might be null in cases** 1. We are to get the respective price impacts for the 10, 100 and 1000 SOL buys or sells 2. It could be because the token’s liquidity does not have enough liquidity for larger values 3. We cannot find the sell quote for the respective token and the buy/sell values # Price API V3 (Beta) Source: https://dev.jup.ag/docs/price/v3 Price API V3 aims to provide a one source of truth across all Jupiter UIs and integrator platforms. The simplified format allows easy integration while letting Jupiter handle the complexity of ensuring the accuracy of the provided prices. **NOTE** * Lite URL: `https://lite-api.jup.ag/price/v3` * Pro URL: `https://api.jup.ag/price/v3` To upgrade to Pro or understand our rate limiting, please refer to this section. **INFO** This is in Beta and subject to changes, if you need help please reach out to us in [Discord](https://discord.gg/jup) ## How Price is Derived Price API V3 price tokens by using the **last swapped price (across all transactions)**. The swaps are priced by working outwards from a small set of reliable tokens (like SOL) whose price we get from external oracle sources. While and also after deriving the last swap price, we also utilize a number of heuristics to ensure the accuracy of the price and eliminate any outliers: * Asset origin and launch method * Market liquidity metrics * Market behaviour patterns * Holder distribution statistics * Trading activity indicators * [Organic Score](/docs/tokens/organic-score) ## Get Price Simply request via the base URL with the query parameters of your desired mint addresses. You can also comma-separate them to request for multiple prices. ```js theme={null} const price = await ( await fetch( 'https://lite-api.jup.ag/price/v3?ids=So11111111111111111111111111111111111111112,JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN' ) ).json(); console.log(JSON.stringify(price, null, 2)); ``` ## Price Response Here is the sample response, notice a few details here: * The `usdPrice` is the only price. * The `decimals` response is helpful to display price information on the UI. * The `blockId` can be used to verify the recency of the price. ```js theme={null} { "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": { "usdPrice": 0.4056018512541055, "blockId": 348004026, "decimals": 6, "priceChange24h": 0.5292887924920519 }, "So11111111111111111111111111111111111111112": { "usdPrice": 147.4789340738336, "blockId": 348004023, "decimals": 9, "priceChange24h": 1.2907622140620008 } } ``` ## Limitations **Query limits** * You can query up to 50 `ids` at once. **If the price of a token cannot be found** * Typically, it is likely that the token has not been traded recently - in the last 7 days. * Additionally, we also use the aforementioned heuristics to determine the price of a token and if the price is reliable - if certain combinations of these factors indicate potential issues with price reliability or market health, the token will be flagged and not provided a price. * The token is flagged as suspicious and this can be cross referenced with the Token API V2's `audit.isSus` field. **V2 had more information** * Yes V2 had more information, however, we think that it is not the best representation of price and it also caused different interpretations of price across the different platforms. * With Price API V3, we are handling the complexities to ensure price accuracy and elimate outliers [using the heuristics as mentioned above](#how-price-is-derived), so there will only be one stable and accurate price source for all. * If you require more information like Price API V2, you can use the `/quote` endpoint of the Swap API to derive those data ([you can refer to this post about how Price API V2 is derived](https://www.jupresear.ch/t/introducing-the-price-v2-api/22175)). # Best Practices Source: https://dev.jup.ag/docs/recurring/best-practices Some best practices when using the Recurring API. | Item | Recommendation | | :---------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Understand the Recurring Product. | The Recurring API supports order creation for both recurring and smart recurring strategies. Understand the difference between the two and choose the appropriate one for your needs. | | Both types of orders require minimum total amount of 100 USD. | As per the Jupiter Recurring API's requirements to prevent small orders from being created.This is similar to jup.ag's frontend check for minimum order amount. | | Time-based orders require minimum number of orders of 2 and 50 USD per order. | As per the Jupiter Recurring API's requirements to prevent small orders from being created.This is similar to jup.ag's frontend check for minimum order amount. | | Token-2022 tokens | The Recurring API does not currently support Token-2022 mints. Ensure you’re only scheduling orders for standard SPL tokens (Token Program) until Token-2022 support is added. | # Cancel Order Source: https://dev.jup.ag/docs/recurring/cancel-order **NOTE** * Lite URL: `https://lite-api.jup.ag/recurring/v1/cancelOrder` * Pro URL: `https://api.jup.ag/recurring/v1/cancelOrder` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Cancel Order If you want to cancel order(s), you need to do these steps: Get a list of the order accounts you want to cancel via `/getRecurringOrders` endpoint. Choose the order account to cancel by making a post request to the `/cancelOrder` endpoint to get the transaction to cancel the order. Sign then send the transaction to the network either via `/execute` endpoint or by yourself. **GET RECURRING ORDERS** [Refer to the `/getRecurringOrders` section](/docs/recurring/get-recurring-orders) to prepare the list of order accounts you want to cancel. **NOTE** The `/cancelOrder` endpoint only supports 1 cancellation per transaction. **CAUTION** Price-based orders via API is deprecated. ```js theme={null} const cancelOrderResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/cancelOrder', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ order: "4DWzP4TdTsuwvYMaMWrRqzya4UTFKFoVjfUWNWh8zhzd", user: wallet.publicKey, recurringType: "time", }), }) ).json(); ``` ## Cancel Order Response **Success Example Response** ```json theme={null} { "requestId": "36779346-ae51-41e9-97ce-8613c8c50553", "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAgORL7cu4ZNuxh1wI9W7GVURyr3A06dH348HDpIQzcAJ4oZOZHXAukWalAX/odOiV55UZa1ePBg8d2tRKQyqCjV6C/H8IQcrfZR4QeOJFykenP3QJznc6vNpqe2D57HTD7Gd1R4MYi595YUO8ViNwpWb17+Q9DxkVcz5fWpSqjtDyiji2RfCl7yoUfzkV42QPexQNFjBK5/+pJhV8QuWShN6r9vLZM5XJNS670dgAgf7wC+wCLLIFWHgjgWx32LJMnJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAabiFf+q4GE+2h/Y0YYwDXaxDncGus7VZig8AAAAAABBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKmMlyWPTiSJ8bs9ECkUjg2DC1oTmdr/EIQEjnvY2+n4WbB1qAZjecpv43A3/wwo1VSm5NY22ehRjP5uuuk/Ujb+tSfUXWQOPsFfYV1bDiOlSpa4PwuCC/cGNfJDSsZAzATG+nrzvtutOj1l82qryXQxsbvkwtL24OR8pgIDRS9dYVCj/auTzJLgPke1v9c3puAy81rBYgsabmuLUTEQsZyVAwcABQL9WQEABwAJA0ANAwAAAAAADA0AAg0IAQQDBQYJCgsMCBYHIWKotyLz" } ``` **Failed Example Response** ```json theme={null} { "code": 400, "error": "Failed to deserialize account data: failed to fill whole buffer", "status": "Bad Request" } ``` ## Execute Cancel Order To sign then send the transaction to the network to execute the cancellation, you can use the `/execute` endpoint or by yourself. Refer to the [Execute Order](/docs/recurring/execute-order) section for more details. # Create Order Source: https://dev.jup.ag/docs/recurring/create-order **NOTE** * Lite URL: `https://lite-api.jup.ag/recurring/v1/createOrder` * Pro URL: `https://api.jup.ag/recurring/v1/createOrder` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Create Order This is a POST request to `/createOrder` endpoint, where you pass in the necessary parameters and our backend will create the transaction for you to sign and send to the network seamlessly. **INFO** The Recurring API supports both Time-based and Price-based (DEPRECATED) strategies. The `createOrder` endpoint is used to create both types of orders based on the parameters you pass in. ### Time-based Order Pass in the **`time`** object in the `params` field. Some notes to help you understand the parameters. * The amount to be spent per cycle is calculated based on your input amount and the total number of orders. ```js theme={null} Amount to be spent per cycle = inAmount / numberOfOrders e.g. 1_000 USDC / 10 orders = 100 USDC per order ``` * The total time to complete is definite as the amount to be spent per cycle is fixed. ```js theme={null} Total time to complete = numberOfOrders * interval e.g. 10 orders * 86_400 seconds = 864_000 seconds = 10 days ``` ```js expandable theme={null} const createOrderResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/createOrder', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ user: wallet.publicKey, inputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", outputMint: "So11111111111111111111111111111111111111112", params: { time: { inAmount: 104000000, // Raw amount of input token to deposit now (before decimals) numberOfOrders: 2, // Total number of orders to execute interval: 86400, // Time between each order in unix seconds minPrice: null, // Minimum price or null maxPrice: null, // Maximum price or null startAt: null, // Unix timestamp of start time or null - null starts immediately }, }, }), }) ).json(); ``` ### Price-based Order (DEPRECATED) Pass in the **`price`** object in the `params` field. **CAUTION** Price-based orders via API is deprecated. **NOTE** Some notes to help you understand the parameters. * Price-based orders are opened indefinitely until the user closes them. * Once low on funds, the order will not be closed and can continue to execute if the user deposits more into the order. Refer to the [Deposit Price Order](/docs/recurring/deposit-price-order) endpoint to deposit more funds into the order. * Alternatively, the user can also withdraw funds from the order without closing it. Refer to the [Withdraw Price Order](/docs/recurring/withdraw-price-order) endpoint to withdraw funds from the order. * Do note that the price-based orders auto withdraws the output tokens to the user's wallet every time the order is executed. * The total time to use up all funds is not definite as the amount to be spent per cycle is variable based on the USDC value of the input token. ```js theme={null} const createOrderResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/createOrder', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ user: wallet.publicKey, inputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", outputMint: "So11111111111111111111111111111111111111112", params: { price: { depositAmount: 110000000, // Raw amount of input token to deposit now (before decimals) incrementUsdcValue: 10000000, // Raw amount of USDC to increment per cycle (before decimals) interval: 86400, // Time between each cycle in unix seconds startAt: null, // Unix timestamp of start time or null - null starts immediately }, }, }), }) ).json(); ``` Now that you have the order transaction, you can sign and send to the network. There are 2 methods, after signing the transaction, you can either send it to the network yourself or use the Recurring API's `/execute` endpoint to do it for you. ## Create Order Response The response from the `createOrder` endpoint is as follows. **INFO** Do note that both time-based and price-based orders will return the same response structure. **Successful Example Response** ```json theme={null} { "requestId": "1d1f3586-eb72-4337-8c7e-1bbb9870ee4b", "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAgNRL7cu4ZNuxh1wI9W7GVURyr3A06dH348HDpIQzcAJ4o8bJlCl2Wc6MzpcvkV0INcJ7u23GV89soNJ/8i5QPLuk+NOvCjbAbTzOyNoSWuhO5fYq+hNGrGQ2JdDy82Gw0bv28tkzlck1LrvR2ACB/vAL7AIssgVYeCOBbHfYskycnT/icRrhr4nbjk0DzDqAkM4ntju8NXHrILEpE0TUKNKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAGm4hX/quBhPtof2NGGMA12sQ53BrrO1WYoPAAAAAAAQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FmwdagGY3nKb+NwN/8MKNVUpuTWNtnoUYz+brrpP1I2/rUn1F1kDj7BX2FdWw4jpUqWuD8Lggv3BjXyQ0rGQMwExvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWG7njQ5EK9zaEM059+IQanso4m+YzpvFchLCtBxOCdR5QcGAAUCGSwAAAYACQNADQMAAAAAAAkGAAMABwUIAQEFAgADDAIAAAAAwusLAAAAAAgBAwERCw0EAAAHDAMBAgUICQoLK453K22iNAuxgF7IZwAAAAAAwusLAAAAAADh9QUAAAAALAEAAAAAAAAAAAAIBAMAAAABCQ==" } ``` **Failed Example Response** ```json theme={null} { "code": 400, "error": "Order is valued at 2.99 USDC, minimum is 100.00 USDC", "status": "Bad Request" } ``` # Deposit Price Order Source: https://dev.jup.ag/docs/recurring/deposit-price-order **NOTE** * Lite URL: `https://lite-api.jup.ag/recurring/v1/priceDeposit` * Pro URL: `https://api.jup.ag/recurring/v1/priceDeposit` To upgrade to Pro or understand our rate limiting, please refer to this section. **CAUTION** Price-based orders via API is deprecated. ## Deposit Order If you want to deposit funds into a price-based order, you need to do these steps: Get a list of the order accounts you want to deposit via `/getRecurringOrders` endpoint. Choose the order account to deposit by making a post request to the `/priceDeposit` endpoint to get the transaction to deposit into the order. Sign then send the transaction to the network either via `/execute` endpoint or by yourself. **GET RECURRING ORDERS** [Refer to the `/getRecurringOrders` section](/docs/recurring/get-recurring-orders) to prepare the order account you want to deposit into. ```js theme={null} const priceDepositResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/priceDeposit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ order: "EpTsCUnKComCd8FDNZn3kVrQBQo2uEn5rRzYk9ocqFPH", user: wallet.publicKey, amount: 1000000 }), }) ).json(); ``` ## Deposit Order Response **Success Example Response** ```json theme={null} { "requestId": "cbc021a6-8a61-49cd-8c5a-9ea29fc2dd4d", "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAcLRL7cu4ZNuxh1wI9W7GVURyr3A06dH348HDpIQzcAJ4ou00rM6bvrYH/o3YhDOZ97jIgg/zdwEtLlVk6ddEK3BXdUeDGIufeWFDvFYjcKVm9e/kPQ8ZFXM+X1qUqo7Q8ozVCa3wbmwfzRz1Av5JAlFtGgdIbvPspoQDO0MABdFvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMGRm/lIRcy/+ytunLDm+e8jOW7xfcSayxDmzpAAAAABt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKkHNtIX+MwRgQakd3fYovqoEXuKqaHTmdCmjuWoQiMib4yXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZxvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWHbZsKfr6NrDjI7Q7M2CqAquH41g9AMbtaLYPfmHMqbN3la+2QyLhVSaIunpVo3X8k4VAEj0cBT/ANSk2IKq9g1BAUABQL3nQAABQAJA0ANAwAAAAAACAYAAgAJBAYBAQcIAAMJAQIGCgcQ8iPGiVLh8rZAQg8AAAAAAA==" } ``` **Failed Example Response** ```json theme={null} { "code": 400, "error": "Failed to deserialize account data: failed to fill whole buffer", "status": "Bad Request" } ``` ## Execute Deposit Order To sign then send the transaction to the network to execute the deposit, you can use the `/execute` endpoint or by yourself. Refer to the [Execute Order](/docs/recurring/execute-order) section for more details. # Execute Order Source: https://dev.jup.ag/docs/recurring/execute-order * Lite URL: `https://lite-api.jup.ag/recurring/v1/execute` * Pro URL: `https://api.jup.ag/recurring/v1/execute` To upgrade to Pro or understand our rate limiting, please refer to this section. After getting the order transaction, you can sign and send to the network yourself or use the Recurring API's `/execute` endpoint to do it for you. ## Sign Transaction Using the Solana `web3.js` v1 library, you can sign the transaction as follows: ```js theme={null} // ... GET /createOrder's response // Extract the transaction from the order response const transactionBase64 = createOrderResponse.transaction // Deserialize the transaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); // Sign the transaction transaction.sign([wallet]); // Serialize the transaction to base64 format const signedTransaction = Buffer.from(transaction.serialize()).toString('base64'); ``` ## Execute Order By making a post request to the `/execute` endpoint, Jupiter executes the order transaction on behalf of you/your users. This includes handling of transaction handling, priority fees, RPC connection, etc. **INFO** Do note that you need both the signed transaction and the order id to execute the order. The order id is returned in the [`createOrder` response](/docs/recurring/create-order). ```js theme={null} const executeResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/execute', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ signedTransaction: signedTransaction, requestId: createOrderResponse.requestId, }), }) ).json(); ``` ## Execute Order Response After making the post request to the `/execute` endpoint, you will receive a response with the status of the order. **Example response of successful order:** ```json theme={null} { "signature": "...", "status": "Success", "order": "4DWzP4TdTsuwvYMaMWrRqzya4UTFKFoVjfUWNWh8zhzd", "error": null } ``` **Example response of failed order:** ```json theme={null} { "signature": "...", "status": "Failed", "order": null, "error": "Insufficient funds for the operation requested.", } ``` ## Send Transaction Yourself If you want to handle the transaction, you can sign and send the transaction to the network yourself. ```js expandable theme={null} const transactionBase64 = createOrderResponse.transaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); transaction.sign([wallet]); const transactionBinary = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 1, skipPreflight: true }); const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "finalized"); if (confirmation.value.err) { throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}\n\nhttps://solscan.io/tx/${signature}`); } else console.log(`Transaction successful: https://solscan.io/tx/${signature}`); ``` # Get Recurring Orders Source: https://dev.jup.ag/docs/recurring/get-recurring-orders **NOTE** * Lite URL: `https://lite-api.jup.ag/recurring/v1/getRecurringOrders` * Pro URL: `https://api.jup.ag/recurring/v1/getRecurringOrders` To upgrade to Pro or understand our rate limiting, please refer to this section. This is a GET request to `/getRecurringOrders` endpoint. The response is paginated for every 10 orders and you can view different pages using the `page` parameter. ## Get Recurring Orders **NOTE** * orderStatus can be either `active` or `history` * recurringType can be either `time` (`price` is deprecated) * includeFailedTx can be either `true` or `false` **CAUTION** Price-based orders via API is deprecated. ## Active Orders To get the active orders, you can pass in the `orderStatus` parameter as `active`. **TIP** You can optionally pass in the input and output token mint addresses to filter the open orders. ```js theme={null} const openOrdersResponse = await ( await fetch( 'https://lite-api.jup.ag/recurring/v1/getRecurringOrders?user=replaceWithPublicKey&orderStatus=active&recurringType=time' ) ).json(); ``` ## Order History To get the order history, you can pass in the `orderStatus` parameter as `history`. ```js theme={null} const orderHistoryResponse = await ( await fetch( 'https://lite-api.jup.ag/recurring/v1/getRecurringOrders?user=replaceWithPublicKey&orderStatus=history&recurringType=price' ) ).json(); ``` # About Recurring API Source: https://dev.jup.ag/docs/recurring/index The Jupiter Recurring API enables you to create automated recurring orders on Solana, allowing users to set up regular token swaps that execute automatically based on time intervals or price conditions. The Recurring API is ideal for: * DeFi applications that want to offer dollar-cost average or value average features * Wallets and platforms looking to provide automated investment options * Projects that want to implement treasury management strategies ## Features | Feature | Description | | :------------------------ | :---------------------------------------------------------------------------------------------------------- | | **Time-based recurring** | Set up regular token swaps that execute automatically at specified time intervals. | | **Price-based recurring** | Create price-based recurring orders that execute when certain market conditions are met. | | **Any token pair** | Create recurring orders between any token pairs supported on Jupiter's Metis Routing Engine. | | **Best execution** | Orders are executed through Jupiter's Metis Routing Engine to get the best possible price across all DEXes. | | **Flexible scheduling** | Configure the frequency and timing of recurring orders to match your needs. | | **Price strategy** | Set a price range in time-based recurring orders. | ## Getting Started with Recurring API 1. [**Create Order**](/docs/recurring/create-order): Create a new recurring order with your desired parameters. 2. [**Cancel Order**](/docs/recurring/cancel-order): Cancel an existing recurring order. 3. [**Deposit in Price-based Orders**](/docs/recurring/deposit-price-order): Deposit funds in price-based orders. 4. [**Withdraw from Price-based Orders**](/docs/recurring/withdraw-price-order): Withdraw funds from price-based orders. 5. [**Get Recurring Orders**](/docs/recurring/get-recurring-orders): Retrieve the history of recurring orders for a specific wallet address. 6. [**Best Practices**](/docs/recurring/best-practices): Best practices for using Recurring API. ## FAQ Recurring API takes 0.1% as fees. Currently no. # Withdraw Price Order Source: https://dev.jup.ag/docs/recurring/withdraw-price-order * Lite URL: `https://lite-api.jup.ag/recurring/v1/priceWithdraw` * Pro URL: `https://api.jup.ag/recurring/v1/priceWithdraw` To upgrade to Pro or understand our rate limiting, please refer to this section. **CAUTION** Price-based orders via API is deprecated. ## Withdraw Order If you want to withdraw funds from a price-based order, you need to do these steps: Get a list of the order accounts you want to withdraw via `/getRecurringOrders` endpoint. Choose the order account to deposit by making a post request to the `/priceDeposit` endpoint to get the transaction to deposit into the order. Sign then send the transaction to the network either via `/execute` endpoint or by yourself. **GET RECURRING ORDERS** [Refer to the `/getRecurringOrders` section](/docs/recurring/get-recurring-orders) to prepare order account you want to withdraw from. **WARNING** If you do not pass in `amount`, the transaction will be built to withdraw the full amount of the order. ```js theme={null} const priceWithdrawResponse = await ( await fetch('https://lite-api.jup.ag/recurring/v1/priceWithdraw', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ order: "EpTsCUnKComCd8FDNZn3kVrQBQo2uEn5rRzYk9ocqFPH", user: wallet.publicKey, inputOrOutput: "In", // either "In" or "Out" mint, note that price-based orders auto withdraws the output tokens to the user's wallet every time the order is executed amount: 1000000 }), }) ).json(); ``` ## Withdraw Order Response **Success Example Response** ```js theme={null} { "requestId": "cb1c0e03-8e4a-4f85-ac36-e353c7981f5b", "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAcNRL7cu4ZNuxh1wI9W7GVURyr3A06dH348HDpIQzcAJ4oHNtIX+MwRgQakd3fYovqoEXuKqaHTmdCmjuWoQiMiby7TSszpu+tgf+jdiEM5n3uMiCD/N3AS0uVWTp10QrcFd1R4MYi595YUO8ViNwpWb17+Q9DxkVcz5fWpSqjtDyjKhKdx27tkl2VPxhBBJcKx9gSuUqMJnrF2JWtuKPpRPM1Qmt8G5sH80c9QL+SQJRbRoHSG7z7KaEAztDAAXRb0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAabiFf+q4GE+2h/Y0YYwDXaxDncGus7VZig8AAAAAABBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKmMlyWPTiSJ8bs9ECkUjg2DC1oTmdr/EIQEjnvY2+n4Wcb6evO+2606PWXzaqvJdDGxu+TC0vbg5HymAgNFL11h22bCn6+jaw4yO0OzNgqgKrh+NYPQDG7Wi2D35hzKmzcjGx2VRtfxzpYauPv7ArfDDH2VHlwLKs45O0rZTboL4wMHAAUCnqwAAAcACQNADQMAAAAAAAEOAAAFCwgCAwEEBgkKDAEStxJGnJRtoSIBQEIPAAAAAAAA" } ``` **Failed Example Response** ```js theme={null} { "code": 400, "error": "Failed to deserialize account data: failed to fill whole buffer", "status": "Bad Request" } ``` ## Execute Withdraw Order To sign then send the transaction to the network to execute the withdrawal, you can use the `/execute` endpoint or by yourself. Refer to the [Execute Order](/docs/recurring/execute-order) section for more details. # Integrate DEX into Metis Source: https://dev.jup.ag/docs/routing/dex-integration Walkthrough on how to integrate your decentralized exchanged (DEX) into Metis upiter is one of the most widely integrated protocols, so a lot of work is involved in minimizing issues on new integrations and making each integration valuable to our users and partners. Our top priority is ensuring security and providing the best prices and the best token selection for our users, so we will focus on DEXes that will bring the most benefits to them. **WE DO NOT CHARGE FEES FOR INTEGRATION.** ## Integration Prerequisites As Solana grows and more DEXes are built, we have to be more cautious in the DEXes we integrate, we look into a variety of factors. * **Code health**: It will help with integration and ensure maintainability in the future. * **Security audit**: This is important to ensure users' funds are secure and the program is not malicious. * **Traction**: We look at the traction of the DEX to ensure it has market demand and is well-used. * **Team and backers**: This is a good indicator of the quality of the DEX if they are backed by or built by reputable or verifiable entities. ### AMM Interface To facilitate integration of your DEX into the Jupiter Core Engine: * Provide a DEX SDK that works with the [Jupiter AMM Interface](https://docs.rs/crate/jupiter-amm-interface). * Enable us to fork your SDK, this ensures our users that we can guarantee maintenance, support for the SDK, and fix potential bugs related to integrated DEXs. **NOTE** `get_accounts_to_update` provides the necessary accounts to fetch, they are batched and cached by the Jupiter Core Engine and delivered through `update` to the AMM instance, there might be multiple calls to `quote` using the same cache so **we do not allow any network calls** in the entire implementation. **RESOURCE AND SUPPORT** You can refer to the implementation guide [https://github.com/jup-ag/rust-amm-implementation](https://github.com/jup-ag/rust-amm-implementation) for easier integration with Jupiter. If you require assistance or have questions, reach out to us at [Discord](https://discord.gg/jup) ```js theme={null} pub trait Amm { // Maybe trait was made too restrictive? fn from_keyed_account(keyed_account: &KeyedAccount, amm_context: &AmmContext) -> Result where Self: Sized; /// A human readable label of the underlying DEX fn label(&self) -> String; fn program_id(&self) -> Pubkey; /// The pool state or market state address fn key(&self) -> Pubkey; /// The mints that can be traded fn get_reserve_mints(&self) -> Vec; /// The accounts necessary to produce a quote fn get_accounts_to_update(&self) -> Vec; /// Picks necessary accounts to update it's internal state /// Heavy deserialization and precomputation caching should be done in this function fn update(&mut self, account_map: &AccountMap) -> Result<()>; fn quote(&self, quote_params: &QuoteParams) -> Result; /// Indicates which Swap has to be performed along with all the necessary account metas fn get_swap_and_account_metas(&self, swap_params: &SwapParams) -> Result; /// Indicates if get_accounts_to_update might return a non constant vec fn has_dynamic_accounts(&self) -> bool { false } /// Indicates whether `update` needs to be called before `get_reserve_mints` fn requires_update_for_reserve_mints(&self) -> bool { false } // Indicates that whether ExactOut mode is supported fn supports_exact_out(&self) -> bool { false } fn get_user_setup(&self) -> Option { None } fn clone_amm(&self) -> Box; /// It can only trade in one direction from its first mint to second mint, assuming it is a two mint AMM fn unidirectional(&self) -> bool { false } /// For testing purposes, provide a mapping of dependency programs to function fn program_dependencies(&self) -> Vec<(Pubkey, String)> { vec![] } fn get_accounts_len(&self) -> usize { 32 // Default to a near whole legacy transaction to penalize no implementation } /// The identifier of the underlying liquidity /// /// Example: /// For RaydiumAmm uses Openbook market A this will return Some(A) /// For Openbook market A, it will also return Some(A) fn underlying_liquidities(&self) -> Option> { None } /// Provides a shortcut to establish if the AMM can be used for trading /// If the market is active at all fn is_active(&self) -> bool { true } } ``` # About Routing Source: https://dev.jup.ag/docs/routing/index The types of routing engines used in Jupiter's Swap product **NOTE** If you are an exchange or market maker and want to participate in our routing system, please refer to our [DEX Integration](/docs/routing/dex-integration) and [RFQ Integration](/docs/routing/rfq-integration) guides. ## Juno Liquidity Engine Juno is Jupiter's latest liquidity engine, it is built with the combined learnings from Metis and JupiterZ, with one single objective - to ensure the best possible execution price and success rate across all engines and liquidity sources. Juno employs a sophisticated self-learning mechanism to maintain high availability of competitive routes while automatically sidelining underperforming or potentially problematic quotes. Juno will be incrementally introducing new third-party liquidity sources and a continual effort to improve Metis and JupiterZ routing capabilities. | | | | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Multi-liquidity sources** | Integrates third-party liquidity sources and Jupiter's proprietary routing engines to ensure best possible rates. Currently, Juno consists of Metis, JupiterZ, Hashflow, DFlow and more in the pipeline. | | **Self-learning** | Automatically detects and sidelines underperforming or problematic sources, while continuously learning to provide competitive quotes. | | **Continuous optimizations** | By integrating external liquidity sources directly, Juno creates a competitive environment that drives continuous improvement across all routing engines. This approach ensures users consistently receive optimal execution rates while providing valuable performance data to enhance both Metis and JupiterZ routing capabilities. | Juno is directly powering the Ultra Swap on Jupiter frontend (jup.ag) and is also accessible via [**Ultra Swap API**](/docs/ultra): The Jupiter Ultra Swap API is the *only* API you ever need to experience or build the best trading experience on Solana - Jupiter handles all the complexities such as RPCs, slippage, broadcast method and landing rates, all while accessing the best liquidity available through Juno. ## Jupiter Metis Routing Engine Since its inception in 2023, Jupiter's proprietary DEX aggregation engine, Metis v1, has become a cornerstone of Solana blockchain's DeFi ecosystem. Metis functions as a sophisticated aggregation layer for on-chain liquidity, with one single objective - to algorithmically determine the most efficient trade route for any given token pair, considering factors like price, slippage, and quoted-to-executed price across multiple DEXes and AMMs. This ensures users receive the best possible execution price available on-chain at the moment that they wish to trade. As of 2025, we've deployed Metis v1.5 which has experimented with modified algorithms to enable more granular splits and allows for a larger set of tokens to act as intermediate tokens in a route. From our analysis so far, this translates to 4.6x less spread between quoted and executed price. This is a continued effort to experiment with better configurations and algorithms to improve Metis. Read more about Metis's history, learnings and future plans | | | | :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | | **Overcoming SVM constraints** | Employs a sophisticated and efficient transaction construction to enable multi-hop-multi-split swaps. | | **Integrating diverse DEXes** | Utilizes a standardized interface to integrate with a wide range of DEXes, abstracting away the complexities of each individual DEX. | | **Optimizing for price and execution** | Ensuring the quoted price is as close as the actual price, while also ensuring the transaction is executed successfully. | | **Accessing markets immediately and safely** | Employs necessary infrastructure powered by a network of robust RPC nodes to include markets and checks their liquidity in real-time. | The engine integrates with most DEXes on Solana and is accessible via the Swap API. You can find a complete list of supported DEXes via the [/swap/v1/program-id-to-label](https://lite-api.jup.ag/swap/v1/program-id-to-label) endpoint. **Metis v1** Metis v1's impact extends far beyond simple facilitation of on-chain trades. It is the de-facto liquidity engine on Solana, playing an instrumental role in onboarding millions of users to the network and facilitating trillions of dollars in cumulative trading volume. Its stability and general-purpose design have made it a reliable foundation for countless developers and protocols building on Solana. Currently, Metis v1 powers the [**Swap API**](/docs/swap): A robust interface designed for developers and applications requiring programmatic access to Solana's liquidity. It currently has tens of thousands of requests per second, with demonstrated capacity to handle peak loads reaching hundreds of thousands of requests per second. ## JupiterZ (RFQ) Routing Engine Since its launch in 2024, JupiterZ has emerged as a transformative addition to Jupiter's routing capabilities. JupiterZ functions as an RFQ (Request For Quote) system that connects users directly with market makers, enabling market makers to provide competitive quotes for top token pairs. This ensures users receive the best possible execution price available from both on-chain and off-chain liquidity at the moment they wish to trade. | | | | :------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Intent-based architecture** | Employs an intent-based system where users express their desired trade and market makers compete to fulfill it. | | **Integrating diverse market makers** | Utilizes a standardized interface to integrate with multiple market makers, abstracting away the complexities of each liquidity provider. | | **Optimizing for price and execution** | Creates a competitive environment where market makers compete to provide the best quotes, while ensuring the transaction is executed successfully and efficiently. | | **Real-time quote aggregation** | Employs a versatile proxy to collect and compare quotes from multiple routing sources (such as Jupiter Metis v1 and JupiterZ) in real-time. | | **Gasless transactions** | Enables users to execute trades without incurring transaction fees, providing a seamless and cost-effective trading experience. | JupiterZ has been handling a large portion of trades on the Jupiter frontend (jup.ag) - which has demonstrated JupiterZ's reliability and effectiveness in providing competitive quotes and successful trade execution. Currently, JupiterZ is accessible through the **Ultra Swap API**: A streamlined interface that makes it simple for developers and applications to tap into Metis v1, JupiterZ and other routing sources. For more information about JupiterZ, please refer to our [RFQ Integration](/docs/routing/rfq-integration) guide. # Market Listing Source: https://dev.jup.ag/docs/routing/market-listing Understand how markets are listed and maintained on Jupiter. ## Routing Type Jupiter Metis creates routes in the perspective of markets and there are 2 types of routing for all markets. We constantly index for updated market data and newly created markets to check against the rules for market listing to ensure markets are eligible for and remain in the Jupiter Metis routing engine. * We automatically list all new markets that are created on specific DEXes (list is below). * These markets have a grace period, where the liquidity criteria is not applied. * After the grace period has passed, the liquidity criteria will apply (refer to normal routing). * For bonding curves, if it does not graduate after the grace period, it will be removed from routing. * Only when the bonding curve has graduated to a new market, the graduated market will be added to routing. - Meteora Dynamic Bonding Curve - Meteora Dynamic AMM - Meteora DAMM V2 - Meteora DLMM - Raydium - Raydium CLMM - Raydium CPMM - Raydium Launchlab - Pump.fun AMM - Pump.fun - Fluxbeam - Whirlpool - Moonshot - Virtuals - Boop.fun * This is the default for all markets. * Every 30 minutes, we will check the liquidity of the market. * If the liquidity is not enough, we will remove the market from routing. ## Market Liquidity Requirements The market must fit one of the following criteria for it to be routable: 1. **Less than 30% price difference on \$500** Using a benchmark position size of \$500, a user should encounter less than 30% price difference after buying \$500 worth and then selling back on the same market. Price Difference = (\$500 - Final USD value) / \$500 If the price difference is more than 30%, it means that there is insufficient liquidity in the market for the benchmark position size of \$500. 2. **Less than 20% price impact on market** If the above (sell back \$500 worth) fails, we will compare the price per token received from buying \$1000 worth vs the price per token received from buying \$500 worth to calculate price impact. If the price impact is more than 20%, it means that the market is illiquid. If you are unsure if your market has passed the liquidity requirement, you can check your pool directly via the UI of the DEX you created the market on. # Integrate MM into JupiterZ (RFQ) Source: https://dev.jup.ag/docs/routing/rfq-integration Walkthrough on how to integrate your RFQ service into Jupiter's routing system. **CAUTION** The integration requirements are subjected to change and please provide suggestions or feedbacks on ways to improve the integration process. RFQ Flow ## Integration Prerequisites * Host a service that adheres to our RFQ API schema * Provide a webhook for Jupiter to send quotes and swap transactions * Complete end-to-end integration tests * When ready, you will be onboarded to Edge before going live on production **NOTE** Please reach out to us in [Discord](https://discord.gg/jup) in the [Developer Support channel](https://discord.com/channels/897540204506775583/910250162402779146) * If you are interested to participate in Jupiter Z * If you need any help or clarification regarding the integration. * To begin onboarding to Edge. ### Example Integration To facilitate the integration, we provide an [integration SDK in this repository](https://github.com/jup-ag/rfq-webhook-toolkit). * [**Sample server**](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/server-example/): Implements the webhook API in Rust. * [**API Schema**](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/openapi): OpenAPI schema for the RFQ API. * [**Integration tests**](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/tests/): Verify the implementation of the webhook. * [**Troubleshooting**](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/tests/README.md#troubleshooting): Common issues that arise during integration. ### RFQ API Schema To facilitate the integration into Jupiter's RFQ module, you will need to provide a webhook for us to register the quotation and swap endpoints with the corresponding request and response format. | Endpoint | Method | URL | Description | | :------- | :----- | :------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | | Base URL | - | `https://your-api-endpoint.com/jupiter/rfq` | Example URL that we will register into our API. | | Quote | POST | `https://your-api-endpoint.com/jupiter/rfq/quote` | Called to request quotes. | | Swap | POST | `https://your-api-endpoint.com/jupiter/rfq/swap` | Called to execute swaps. | | Tokens | GET | `https://your-api-endpoint.com/jupiter/rfq/tokens` | Called periodically to fetch supported tokens ([see the token section below](#advertising-supported-tokens)). | **API KEY** If you require an API key to access your endpoints, please provide it to us during the registration process. The API Key will be passed to the webhook as a header `X-API-KEY`. ### Response Codes Market Makers should return appropriate HTTP status codes along with error messages. | Status Code | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------- | | `200 OK` | The request was successful, and the webhook will return a quote. | | `404 Not Found` | The webhook will not return a quote for this request (e.g. the pair or the size are not supported). | | `400 Bad Request` | The request sent to the webhook is malformed (e.g. missing an expected parameter). | | `401 Unauthorized` | Authorization failed. For example the `X-API-KEY` is missing or incorrect. | | `50x Server Errors` | The webhook is offline or unable to respond. If the status persist, the webhook will be temporarily suspended and will not receive requests. | **TIMEOUTS** A webhook must adhere to the [fulfillment and response time requirements](#fulfillment-requirements). When sending the quote request, the RFQ system includes the following headers: | Header | Description | | :--------------------------------------------------------- | :----------------------------------------------------------------- | | `x-request-start` | The millisecond timestamp indicating when the request was sent. | | `x-request-timeout` | The millisecond timeout for the request (currently set to 250 ms). | ## Integration Notes ### Order Engine The RFQ functionality depends on the mainnet deployment of the [Order Engine Program](https://solscan.io/account/61DFfeTKM7trxYcPQCM78bJ794ddZprZpAwAnLiwTpYH) for order fulfillment. * **Source Code**: The program's source is located in the [programs/order-engine](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/programs/order-engine) directory. * **IDL**: The Interface Definition Language (IDL) file is available [here](https://github.com/jup-ag/rfq-webhook-toolkit/tree/main/idls). ### Fulfillment Requirements To ensure market makers stay competitive and responsive, we enforce a minimum benchmark for fulfillment and response times. * **Fulfillment**: Market makers are expected to comply and fulfill **95%** of the quotes provided within a 1-hour window. If this is not met, the market maker will be turned off. * **Response Time**: A webhook must respond within **250 ms** of receiving a quote request. If it fails to do so, the RFQ system will proceed with the available quotes at the time. **CAUTION** To resume operations, we will need to manually re-enable your webhook, please reach out to us if this happens. ### Expiry We enforce a fixed expiry timing flow for all quotes and transactions. This simplifies the integration by removing the need for market makers to specify custom expiry times in quote requests, providing consistent behavior across all quotes and transactions, and establishing clear timeout boundaries at different stages of the flow. **Breakdown of the expiry flow:** * **Total of 50 seconds**: Transaction expiry time * **1st 25 seconds**: Reserved for the webhook to verify, sign, and send the transaction on-chain * **2nd 25 seconds**: Allocated for the user to accept the quote **NOTE** The frontend automatically re-quotes every 5 seconds. **CAUTION** These expiry thresholds may be adjusted based on performance and feedback. ### Fees Jupiter RFQ allows MMs a way to provide liquidity, adjust their quotes without being subject to the volatility of on-chain gas prices or chain health. RFQ fills are also much less CU intensive (\< 10x) compared to AMM swaps, and can save gas in the long run on fills. Today, RFQ, when operating in Ultra mode, charges a dynamic fee that is selected based on factors like tokens and size. **Dynamic Fee** The dynamic fee amount is forwarded to webhooks in the quote request parameters and it is contained in the message that both taker and maker sign ([see the payload section below](#non-standard-payload)). In manual mode, the fee is a flat 2pbs. **Fee Calculation** Webhooks do not need to account for fees when quoting, the fee is applied directly by the RFQ system during transaction building. * For example, for a quote of 1 SOL to 1,000 USDC with a fee of 100 bps * Only 990 USDC will be transferred out of the market maker account * While 10 USDC will be collected as a fee **NOTE** The fee is not automatically transferred and will be accounted for asynchronously on a regular basis. This is subject to change in the future. ### Non-standard payload The transaction data includes, beside the instruction data for the order-engine, 3 additional bytes that are appended to the instruction data. These bytes are not processed by the program and are only information and to be consumed by an off-chain consumer. The first 2 bytes contains the fee amount in basis points (u16) and the third byte (u8) is a bit mask where the least significant bit indicates if the swap is exact-in (0) or exact-out (1). ### Advertising Supported Tokens In order to receive relevant quote requests, market makers need to advertise the tokens they support. This is done by providing a list of supported tokens in the response to the `/tokens` route. The response should be a JSON array of token addresses. The list of tokens is refreshed every 10 minutes. ## FAQ Yes, native SOL is fully supported in the order-engine program for both the taker (user) and the maker. However, for now, we assume the maker will use WSOL (Wrapped SOL). No, the RFQ system dispatches the quote request to all registered webhooks simultaneously. All quotes received within the quote timeout are compared to select the best one. The selection prioritizes the quote value first (In the unlikely scenario where two quotes have identical values, the quote from the webhook with the faster response time will be actually prioritized). Yes, the RFQ system will verify the swap requests before forwarding them to the webhooks. However, webhooks are encouraged to verify the swap requests as well to ensure the integrity of the system. The checks that the RFQ system performs can be found in the validate\_similar\_fill\_sanitized\_message function. No, there is no penalty. It is up to the webhook to decide whether to respond with a quote (200 OK) or indicate that it cannot provide one (404 Not Found). For example, suppose a webhook provides quotes for USDC/SOL only within a range of 100 to 1000 USDC. If it receives a quote request for 10 USDC → SOL, it will respond with 404 Not Found, since the amount is outside its quoting range. In another case, a webhook may only support one-way quotes (USDC → SOL) but not SOL → USDC. If it receives a request for SOL → USDC, it will also return 404 Not Found. No. Stable to stable swaps are exempt from fees. # Token Listing Source: https://dev.jup.ag/docs/routing/token-listing Understand how tokens are listed and verified on Jupiter. Jupiter's routing engine creates routes in the perspective of markets, if you have minted/created a token but does not have a [market created with liquidity](/docs/routing/market-listing), your token will not be tradable. To understand more about Tokens in Jupiter's products, please refer to the following resources. Learn more about Jupiter's token listing and verification process. Use Jupiter's Tokens API to search for and get token information. To verify your token, please submit your token to the Verify website. # Craft Clawback (Beta) Source: https://dev.jup.ag/docs/send/craft-clawback **NOTE** * Lite URL: `https://lite-api.jup.ag/send/v1` * Pro URL: `https://api.jup.ag/send/v1` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Overview Load invite code. Load public key from invite. Find the [Program Derived Address (PDA)](https://solana.com/core/pda) of the invite. * Uses `"invite"` and the public key of recipient at seed. Post request to get Clawback transaction. Sign with sender keypair, then send transaction and wait for confirmation. **NOTE** [Please ensure that you have set up the prerequisites](/docs/send/invite-code#overview). ```js theme={null} import { invite_code_to_priv_key } from "./utils.js"; import { Connection, Keypair, PublicKey, VersionedTransaction, } from "@solana/web3.js"; import fs from "fs"; const connection = new Connection('insert-rpc'); const senderPrivateKey = JSON.parse(fs.readFileSync('/Path/to/sender/id.json', 'utf8').trim()); const sender = Keypair.fromSecretKey(new Uint8Array(senderPrivateKey)); process.loadEnvFile('.env'); // STEP 1: Load invite code const invite_code = process.env.INVITE_CODE; // STEP 2: Load the public key from the invite code const secret_key = invite_code_to_priv_key(invite_code); const pubkey = Keypair.fromSecretKey(secret_key).publicKey; // STEP 3: Find the Program Derived Address (PDA) for the invite // Uses `"invite"` as seed + the public key // PDAs are deterministic addresses owned by the program const invite_pda = PublicKey.findProgramAddressSync( [Buffer.from("invite"), pubkey.toBuffer()], new PublicKey("inv1tEtSwRMtM44tbvJGNiTxMvDfPVnX9StyqXfDfks") )[0]; // STEP 4: Post request for a Clawback transaction const craftClawbackTransaction = await ( await fetch ('https://lite-api.jup.ag/send/v1/craft-clawback', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ invitePDA: invite_pda.toBase58(), sender: sender.publicKey.toBase58(), }, null, 2) }) ).json(); // STEP 5: Use sender keypair to sign and send to network const transaction = VersionedTransaction.deserialize(Buffer.from(craftClawbackTransaction.tx, 'base64')); transaction.sign([sender]); // SIGN with SENDER const transactionBinary = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "confirmed" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); // Log the signature immediately after sending, before confirmation console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "confirmed"); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); } } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); } ``` ## Imports ```js theme={null} import { invite_code_to_priv_key } from "./utils.js"; import { Connection, Keypair, PublicKey, VersionedTransaction, } from "@solana/web3.js"; import fs from "fs"; const connection = new Connection('insert-rpc'); const senderPrivateKey = JSON.parse(fs.readFileSync('/Path/to/sender/id.json', 'utf8').trim()); const sender = Keypair.fromSecretKey(new Uint8Array(senderPrivateKey)); process.loadEnvFile('.env'); ``` ## Invite Code and Public Key ```js theme={null} // STEP 1: Load invite code const invite_code = process.env.INVITE_CODE; // STEP 2: Load the public key from the invite code const secret_key = invite_code_to_priv_key(invite_code); // Follow the utils.js guide const pubkey = Keypair.fromSecretKey(secret_key).publicKey; ``` ## Invite PDA ```js theme={null} // STEP 3: Find the Program Derived Address (PDA) for the invite // Uses `"invite"` as seed + the public key // PDAs are deterministic addresses owned by the program const invite_pda = PublicKey.findProgramAddressSync( [Buffer.from("invite"), pubkey.toBuffer()], new PublicKey("inv1tEtSwRMtM44tbvJGNiTxMvDfPVnX9StyqXfDfks") )[0]; ``` ## Craft Clawback **NOTE** The clawback will return the full amount including leftover transaction fees and/or rent back to the sender. ```js theme={null} // STEP 4: Post request for a Clawback transaction const craftClawbackTransaction = await ( await fetch ('https://lite-api.jup.ag/send/v1/craft-clawback', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ invitePDA: invite_pda.toBase58(), sender: sender.publicKey.toBase58(), }, null, 2) }) ).json(); ``` # Craft Send (Beta) Source: https://dev.jup.ag/docs/send/craft-send **NOTE** * Lite URL: `https://lite-api.jup.ag/send/v1` * Pro URL: `https://api.jup.ag/send/v1` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Overview Create invite code. From utils, derive the secret key - a deterministic 64-byte Solana secret key (32 bytes private + 32 bytes public key). Create Solana Keypair instance from the secret key. Post request to get Send transaction. Sign with both sender and recipient keypair, then send transaction and wait for confirmation. **NOTE** [Please ensure that you have set up the prerequisites](/docs/send/invite-code#overview). ```js theme={null} import { create_invite_code, invite_code_to_priv_key } from "./utils.js"; import { Connection, Keypair, VersionedTransaction, } from "@solana/web3.js"; import fs from "fs"; const connection = new Connection('insert-rpc'); const senderPrivateKey = JSON.parse(fs.readFileSync('/Path/to/sender/id.json', 'utf8').trim()); const sender = Keypair.fromSecretKey(new Uint8Array(senderPrivateKey)); // STEP 1: Create 12-character invite code const invite_code = await create_invite_code(); // STEP 2: Derive secret key (public and private key) const secret_key = invite_code_to_priv_key(invite_code); // STEP 3: Use secret key to create Solana Keypair instance const recipient = Keypair.fromSecretKey(secret_key); // STEP 4: Post request for a Send transaction const craftSendTransaction = await ( await fetch ('https://lite-api.jup.ag/send/v1/craft-send', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ inviteSigner: recipient.publicKey.toBase58(), sender: sender.publicKey.toBase58(), amount: "10000000", // atomic amount before decimals // mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Defaults to SOL if `mint` is not provided }, null, 2) }) ).json(); // STEP 5: Use sender and receipient keypair to sign and send to network const transaction = VersionedTransaction.deserialize(Buffer.from(craftSendTransaction.tx, 'base64')); transaction.sign([sender, recipient]); // SIGN with both SENDER and RECIPIENT keypair const transactionBinary = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "confirmed" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); // Log the signature immediately after sending, before confirmation console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "confirmed"); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); }; } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); }; ``` ## Imports ```js theme={null} import { create_invite_code, invite_code_to_priv_key } from "./utils.js"; import { Connection, Keypair, } from "@solana/web3.js"; import fs from "fs"; const connection = new Connection('insert-rpc'); const senderPrivateKey = JSON.parse(fs.readFileSync('/Path/to/sender/id.json', 'utf8').trim()); const sender = Keypair.fromSecretKey(new Uint8Array(senderPrivateKey)); ``` ## Create Invite Code ```js theme={null} // STEP 1: Create 12-character invite code const invite_code = await create_invite_code(); // STEP 2: Derive secret key (public and private key) const secret_key = invite_code_to_priv_key(invite_code); // STEP 3: Use secret key to create Solana Keypair instance const recipient = Keypair.fromSecretKey(secret_key); ``` ## Craft Send **API PARAMS** * The `amount` is in its atomic value before applying decimals, e.g. 1 USDC is 1\_000\_000. * The `mint` defaults to SOL if not provided, if provided it can be any token mint. **SIGNING AND SENDING** * After getting the transaction, you need to sign with **both sender and recipient** keypair. * You can send the transaction to the network via any method. ```js theme={null} // STEP 4: Post request for a Send transaction const craftSendTransaction = await ( await fetch ('https://lite-api.jup.ag/send/v1/craft-send', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ inviteSigner: recipient.publicKey.toBase58(), sender: sender.publicKey.toBase58(), amount: "10000000", // mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, null, 2) }) ).json(); ``` # About Send API Source: https://dev.jup.ag/docs/send/index Send is the perfect onboarding tool to gift, pay, or onboard anyone in seconds - even if they don't have a wallet. * Send any token - SOL, USDC or memecoins. * Send to a new user without a wallet, existing user or anyone. * No fees to send or claim, only network transaction fees required. * Use Jupiter Mobile seamlessly - even if you sent non-SOL tokens, Ultra provides Gasless Support that pays for swap transaction fees. ## About Send API provides more opportunities for potential users to be onboarded from other websites, apps, or any where else! * API only supports creating Send and Clawback transactions. * Claiming needs to be done via Jupiter Mobile only. * You can gamify the experience post-claim once they are onboarded! ## Jupiter Mobile Adapter To maximize users experience, once your users have claimed via Jupiter Mobile, they can use the app to continue their journey on your app or other use cases. This can be done via [Jupiter Mobile Adapter](/tool-kits/wallet-kit/jupiter-mobile-adapter), allowing Jupiter Mobile users to simply use the app to scan a QR code to login, they can utilize their wallets on Jupiter Mobile across any platform. ## FAQ * The invite code can be in the format of a link or a QR code. * No, Send claims should be done in Jupiter Mobile. * Send is end-to-end self-custodial, where if recipient never claims, the invite code becomes invalid and your tokens are sent back to you upon expiry. * Or use the clawback endpoint via the API to create the clawback transaction. # Invite Code (Beta) Source: https://dev.jup.ag/docs/send/invite-code **NOTE** * Lite URL: `https://lite-api.jup.ag/send/v1` * Pro URL: `https://api.jup.ag/send/v1` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Security The Send API is designed for **transaction building only** - it expects and exchanges parameters such as public keys, amounts, and mint addresses. The API **does not handle private keys or invite codes** for security reasons. **All cryptographic operations must be performed client-side:** * Invite code generation * Private key derivation from invite codes * Transaction signing The following sections provide the complete implementation steps required before using the API. **WARNING** **CRITICAL SECURITY REQUIREMENTS** * **Never share invite codes or private keys** - treat them like passwords or seed phrases * **Store invite codes securely** - use encrypted storage, secure vaults, or environment variables * **Validate all inputs** - ensure invite codes meet expected format before processing * **Implement proper error handling** - avoid exposing sensitive data in logs or error messages **⚠️ Loss of funds:** Any exposure of invite codes or private keys may result in permanent loss of funds. Jupiter is not liable for losses due to compromised credentials. ## Overview Create invite code. From utils, derive the secret key - a deterministic 64-byte Solana secret key (32 bytes private + 32 bytes public key). Create Solana Keypair instance from the secret key. Post request to get Send transaction. * If `craft-clawback`, requires an additional `invitePDA` to be passed in. Sign with both sender and recipient keypair, then send transaction and wait for confirmation. ```js theme={null} import crypto from "crypto"; import * as ed from "@noble/ed25519"; import { sha512 } from "@noble/hashes/sha512"; const hashFunction = (...messages) => sha512(ed.etc.concatBytes(...messages)); ed.etc.sha512Sync = hashFunction; const { createHash } = await import("node:crypto"); // This function creates a random 12-character base58 invite code // Uses 13 random bytes (~1.4 quintillion possible codes) export async function create_invite_code() { const buf = crypto.randomBytes(13); // 58^12 = 1.449225352 e21 return binary_to_base58(new Uint8Array(buf)).substring(0, 12); }; // This function converts an invite code to a deterministic private key // Uses SHA256 hash of `"invite:"` + `invite_code` as the seed // Returns a 64-byte Solana keypair (32 bytes private + 32 bytes public key) export function invite_code_to_priv_key(invite_code) { // Hash the invite code with a prefix const pre_hash = "invite:" + invite_code; const sha = createHash("sha256"); const priv_key = crypto.createHash("sha256").update(pre_hash).digest(); // Use ed25519 to get the public key const pub_key = ed.getPublicKey(new Uint8Array(priv_key)); const solana_priv_key = new Uint8Array(64); solana_priv_key.set(priv_key); solana_priv_key.set(pub_key, 32); return solana_priv_key; }; ///////////////////////////////////////////////////////////////////////////////////// // Taken from https://github.com/pur3miish/base58-js const base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; const create_base58_map = () => { const base58M = Array(256).fill(-1); for (let i = 0; i < base58_chars.length; ++i) base58M[base58_chars.charCodeAt(i)] = i; return base58M; }; const base58Map = create_base58_map(); export function binary_to_base58(uint8array) { const result = []; for (const byte of uint8array) { let carry = byte; for (let j = 0; j < result.length; ++j) { const x = (base58Map[result[j]] << 8) + carry; result[j] = base58_chars.charCodeAt(x % 58); carry = (x / 58) | 0; } while (carry) { result.push(base58_chars.charCodeAt(carry % 58)); carry = (carry / 58) | 0; } } for (const byte of uint8array) if (byte) break; else result.push("1".charCodeAt(0)); result.reverse(); return String.fromCharCode(...result); } export function base58_to_binary(base58String) { if (!base58String || typeof base58String !== "string") throw new Error(`Expected base58 string but got “${base58String}”`); if (base58String.match(/[IOl0]/gmu)) throw new Error( `Invalid base58 character “${base58String.match(/[IOl0]/gmu)}”` ); const lz = base58String.match(/^1+/gmu); const psz = lz ? lz[0].length : 0; const size = ((base58String.length - psz) * (Math.log(58) / Math.log(256)) + 1) >>> 0; return new Uint8Array([ ...new Uint8Array(psz), ...base58String .match(/.{1}/gmu) .map((i) => base58_chars.indexOf(i)) .reduce((acc, i) => { acc = acc.map((j) => { const x = j * 58 + i; i = x >> 8; return x; }); return acc; }, new Uint8Array(size)) .reverse() .filter( ( (lastValue) => (value) => (lastValue = lastValue || value) )(false) ), ]); } ///////////////////////////////////////////////////////////////////////////////////// ``` ```js theme={null} import { create_invite_code, invite_code_to_priv_key } from "./utils.js"; import { Connection, Keypair, VersionedTransaction, } from "@solana/web3.js"; import fs from "fs"; const connection = new Connection('insert-rpc'); const senderPrivateKey = JSON.parse(fs.readFileSync('/Path/to/sender/id.json', 'utf8').trim()); const sender = Keypair.fromSecretKey(new Uint8Array(senderPrivateKey)); // STEP 1: Create 12-character invite code const invite_code = await create_invite_code(); // STEP 2: Derive secret key (public and private key) const secret_key = invite_code_to_priv_key(invite_code); // STEP 3: Use secret key to create Solana Keypair instance const recipient = Keypair.fromSecretKey(secret_key); // STEP 4: Post request for a Send transaction const craftSendTransaction = await ( await fetch ('https://lite-api.jup.ag/send/v1/craft-send', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ inviteSigner: recipient.publicKey.toBase58(), sender: sender.publicKey.toBase58(), amount: "10000000", // atomic amount before decimals // mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Defaults to SOL if `mint` is not provided }, null, 2) }) ).json(); // STEP 5: Use sender and receipient keypair to sign and send to network const transaction = VersionedTransaction.deserialize(Buffer.from(craftSendTransaction.tx, 'base64')); transaction.sign([sender, recipient]); // SIGN with both SENDER and RECIPIENT keypair const transactionBinary = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "confirmed" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); // Log the signature immediately after sending, before confirmation console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "confirmed"); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); }; } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); }; ``` ## Prerequisite ### Dependencies ```bash theme={null} npm install @solana/web3.js@1 # Using v1 of web3.js instead of v2 npm install dotenv # Useful for testing and handling of invite code and private key npm install @noble/ed25519 npm install @noble/hashes ``` ### Imports Create a utils file to add these functions ```js theme={null} import crypto from "crypto"; import * as ed from "@noble/ed25519"; import { sha512 } from "@noble/hashes/sha512"; import { PublicKey } from "@solana/web3.js"; // Configure the ed25519 library to use SHA-512 for internal operations // This is REQUIRED before using any ed25519 functions like getPublicKey() // The library needs to know which hash function to use for key derivation and signing const hashFunction = (...messages) => sha512(ed.etc.concatBytes(...messages)); ed.etc.sha512Sync = hashFunction; // Import createHash function from Node.js crypto module using dynamic import // This allows us to use the modern 'node:crypto' protocol for better compatibility // createHash is used for SHA-256 hashing in the invite code functions const { createHash } = await import("node:crypto"); ``` ## Functions ### Create Invite Code ```js theme={null} // This function creates a random 12-character base58 invite code // Uses 13 random bytes (~1.4 quintillion possible codes) export async function create_invite_code() { const buf = crypto.randomBytes(13); // 58^12 = 1.449225352 e21 return binary_to_base58(new Uint8Array(buf)).substring(0, 12); }; ``` ### Derive Solana Secret Key ```js theme={null} // This function converts an invite code to a deterministic private key // Uses SHA256 hash of `"invite:"` + `invite_code` as the seed // Returns a 64-byte Solana secret key (32 bytes private + 32 bytes public key) export function invite_code_to_priv_key(invite_code) { // Hash the invite code with a prefix const pre_hash = "invite:" + invite_code; const sha = createHash("sha256"); const priv_key = crypto.createHash("sha256").update(pre_hash).digest(); // Use ed25519 to get the public key const pub_key = ed.getPublicKey(new Uint8Array(priv_key)); const solana_priv_key = new Uint8Array(64); solana_priv_key.set(priv_key); solana_priv_key.set(pub_key, 32); return solana_priv_key; }; ``` ### Convert Binary To Base58 ```js expandable theme={null} ///////////////////////////////////////////////////////////////////////////////////// // Taken from https://github.com/pur3miish/base58-js const base58_chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; const create_base58_map = () => { const base58M = Array(256).fill(-1); for (let i = 0; i < base58_chars.length; ++i) base58M[base58_chars.charCodeAt(i)] = i; return base58M; }; const base58Map = create_base58_map(); export function binary_to_base58(uint8array) { const result = []; for (const byte of uint8array) { let carry = byte; for (let j = 0; j < result.length; ++j) { const x = (base58Map[result[j]] << 8) + carry; result[j] = base58_chars.charCodeAt(x % 58); carry = (x / 58) | 0; } while (carry) { result.push(base58_chars.charCodeAt(carry % 58)); carry = (carry / 58) | 0; } } for (const byte of uint8array) if (byte) break; else result.push("1".charCodeAt(0)); result.reverse(); return String.fromCharCode(...result); } export function base58_to_binary(base58String) { if (!base58String || typeof base58String !== "string") throw new Error(`Expected base58 string but got “${base58String}”`); if (base58String.match(/[IOl0]/gmu)) throw new Error( `Invalid base58 character “${base58String.match(/[IOl0]/gmu)}”` ); const lz = base58String.match(/^1+/gmu); const psz = lz ? lz[0].length : 0; const size = ((base58String.length - psz) * (Math.log(58) / Math.log(256)) + 1) >>> 0; return new Uint8Array([ ...new Uint8Array(psz), ...base58String .match(/.{1}/gmu) .map((i) => base58_chars.indexOf(i)) .reduce((acc, i) => { acc = acc.map((j) => { const x = j * 58 + i; i = x >> 8; return x; }); return acc; }, new Uint8Array(size)) .reverse() .filter( ( (lastValue) => (value) => (lastValue = lastValue || value) )(false) ), ]); } ///////////////////////////////////////////////////////////////////////////////////// ``` # Manage Invites (Beta) Source: https://dev.jup.ag/docs/send/manage-invites **NOTE** * Lite URL: `https://lite-api.jup.ag/send/v1` * Pro URL: `https://api.jup.ag/send/v1` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Overview Get pending invites. Get invite history. **NOTE** Both of the following endpoints only returns the invites that are set up by the sender and not from the perspective of the recipient. * Pending invites: Invites created by the sender that are not yet expired and can be clawback/claimed. * Invite history: Invites created by the sender and is either claimed, clawback, or expired. (You can also pass in a Recipient pubkey to get their history) **TIP** Depending on how you have set up to allow connection of wallets, either via [Jupiter Mobile Adapter](/tool-kits/wallet-kit/jupiter-mobile-adapter) for QR code login, wallet extensions, or any other methods, you will need to handle the passing in of their pubkey to the API to get the necessary data. ## Get Pending Invites ```js theme={null} const pendingInvites = await ( await fetch( `https://lite-api.jup.ag/send/v1/pending-invites?address=${pubkey}` ) ).json(); ``` ## Get Invite History ```js theme={null} const inviteHistory = await ( await fetch( `https://lite-api.jup.ag/send/v1/invite-history?address=${pubkey}` ) ).json(); ``` # Claim Fee (Beta) Source: https://dev.jup.ag/docs/studio/claim-fee **NOTE** * Lite URL: `https://lite-api.jup.ag/studio/v1`: 100 requests per 5 minutes * Pro URL: `https://api.jup.ag/studio/v1`: 10 requests per 10 seconds (for all Tiers) To upgrade to Pro or understand our rate limiting, please refer to this section. **API REFERENCE** To fully utilize the Studio API, check out the [Studio API Reference](/api-reference/studio). ## Prerequisite ```bash theme={null} npm install @solana/web3.js@1 # Using v1 of web3.js instead of v2 npm install dotenv # If required for wallet setup ``` **Set up RPC** **NOTE** Solana provides a [default RPC endpoint](https://solana.com/docs/core/clusters). However, as your application grows, we recommend you to always use your own or provision a 3rd party provider’s RPC endpoint such as [Helius](https://helius.dev/) or [Triton](https://triton.one/). ```js theme={null} import { Connection } from '@solana/web3.js'; const connection = new Connection('https://api.mainnet-beta.solana.com'); ``` **Set up Development Wallet** **NOTE** * You can paste in your private key for testing purposes but this is not recommended for production applications. * If you want to store your private key in the project directly, you can do it via a `.env` file. To set up a development wallet via `.env` file, you can use the following script. ```js theme={null} // index.js import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || '')); ``` ```bash theme={null} # .envPRIVATE_KEY='' ``` To set up a development wallet via a wallet generated via [Solana CLI](https://solana.com/docs/intro/installation#solana-cli-basics), you can use the following script. ```js theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/To/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` ```js theme={null} transaction.sign([wallet]); const transactionBinary = transaction.serialize(); console.log(transactionBinary); console.log(transactionBinary.length); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: 'finalized' }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 0, skipPreflight: true, }); console.log(`Transaction sent: https://solscan.io/tx/${signature}`); try { const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, 'confirmed'); if (confirmation.value.err) { console.error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`); console.log(`Examine the failed transaction: https://solscan.io/tx/${signature}`); } else { console.log(`Transaction successful: https://solscan.io/tx/${signature}`); } } catch (error) { console.error(`Error confirming transaction: ${error}`); console.log(`Examine the transaction status: https://solscan.io/tx/${signature}`); }; ``` ## Pool Address Your successfully created token via Jupiter Studio, should have a newly generated token mint. By using the mint, you can get the config key and pool addresses associated to it: Dynamic Bonding Curve pool and Meteora DAMM V2 pool. ```js theme={null} const poolAddressResponse = await ( await fetch( `https://lite-api.jup.ag/studio/v1/dbc-pool/addresses/${mint}`, ) ).json(); ``` ## Fee Using the Pool Address, you will be able to get the total and current unclaimed fees in the Dynamic Bonding Curve pool. ```js theme={null} const feeResponse = await ( await fetch ( 'https://lite-api.jup.ag/studio/v1/dbc/fee', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ poolAddress: poolAddressResponse.data.dbcPoolAddress, }, null, 2) }) ).json(); ``` ## Claim Fee In order to claim fees from a Dynamic Bonding Curve pool, you will need to pass in the pool address into this endpoint and we will create the Claim Fee transaction for you. After receiving the transaction, you will need to sign and submit the transaction to the network on your own ([refer to Transaction Sending Example above](#prerequisite)). ```js theme={null} const claimTransaction = await ( await fetch ( 'https://lite-api.jup.ag/studio/v1/dbc/fee/create-tx', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ownerWallet: wallet.publicKey.toBase58(), poolAddress: poolAddressResponse.data.dbcPoolAddress, maxQuoteAmount: 1000000, // e.g. 1 USDC (depending on quote mint and decimals) }, null, 2) }) ).json(); ``` # Create Token (Beta) Source: https://dev.jup.ag/docs/studio/create-token **NOTE** * Lite URL: `https://lite-api.jup.ag/studio/v1`: 100 requests per 5 minutes * Pro URL: `https://api.jup.ag/studio/v1`: 10 requests per 10 seconds (for all Tiers) To upgrade to Pro or understand our rate limiting, please refer to this section. **API REFERENCE** To fully utilize the Studio API, check out the [Studio API Reference](/api-reference/studio). ## Prerequisite ```bash theme={null} npm install @solana/web3.js@1 # Using v1 of web3.js instead of v2 npm install dotenv # If required for wallet setup ``` **Set up Development Wallet** **NOTE** * You can paste in your private key for testing purposes but this is not recommended for production applications. * If you want to store your private key in the project directly, you can do it via a `.env` file. To set up a development wallet via `.env` file, you can use the following script. ```js theme={null} // index.js import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || '')); ``` ```bash theme={null} # .env PRIVATE_KEY='' ``` To set up a development wallet via a wallet generated via [Solana CLI](https://solana.com/docs/intro/installation#solana-cli-basics), you can use the following script. ```js theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/To/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` ## Create Transaction This endpoint helps you create a few key components to launch your token on Studio. 1. `transaction`: A base64-encoded unsigned transaction. 2. `mint`: The mint of the token that is being created. 3. `imagePresignedUrl`: A `PUT` request endpoint to upload your token image. 4. `metadataPresignedUrl`: A `PUT` request endpoint to upload your token metadata. 5. `imageUrl`: The token's static image url to be used in the metadata. **PRESETS** On [https://jup.ag/studio](https://jup.ag/studio), you can find a few different presets to get you started. **Great for memes, similar profile to traditional meme launches.** * People begin buying your token at 16K Market Cap (MC) in USDC. * It graduates to a Meteora pool at 69K MC. * Your pool raises \~17.94K USDC before graduation. ```js theme={null} buildCurveByMarketCapParam: { quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', initialMarketCap: 16000, migrationMarketCap: 69000, tokenQuoteDecimal: 6, lockedVestingParam: { totalLockedVestingAmount: 0, cliffUnlockAmount: 0, numberOfVestingPeriod: 0, totalVestingDuration: 0, cliffDurationFromMigrationTime: 0, }, }, antiSniping: false, fee: { feeBps: 100, }, isLpLocked: true, tokenName: '', tokenSymbol: '', tokenImageContentType: 'image/jpeg', creator: wallet.publicKey.toBase58(), ``` **For projects ready to take it up a notch. More capital required to bond, but you'll have deeper liquidity and more LP fees when you graduate.** * People begin buying your token at 32k Market Cap (MC) in USDC. * It graduates to a Meteora pool at 240k MC. * Your pool raises \~57.78K USDC before graduation. * 10% of total supply will be vested daily over 12 months. ```js theme={null} buildCurveByMarketCapParam: { quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', initialMarketCap: 32000, migrationMarketCap: 240000, tokenQuoteDecimal: 6, lockedVestingParam: { totalLockedVestingAmount: 100000000, cliffUnlockAmount: 0, numberOfVestingPeriod: 365, totalVestingDuration: 31536000, cliffDurationFromMigrationTime: 0, }, }, antiSniping: true, fee: { feeBps: 100, }, isLpLocked: true, tokenName: '', tokenSymbol: '', tokenImageContentType: 'image/jpeg', creator: wallet.publicKey.toBase58(), ``` Just pass in the parameters you need! ```js theme={null} const createTransaction = await ( await fetch ( 'https://lite-api.jup.ag/studio/v1/dbc-pool/create-tx', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ buildCurveByMarketCapParam: { quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // or SOL or JUP initialMarketCap: 16000, // This means 16_000 USDC migrationMarketCap: 69000, // This means 69_000 USDC tokenQuoteDecimal: 6, lockedVestingParam: { totalLockedVestingAmount: 0, cliffUnlockAmount: 0, numberOfVestingPeriod: 0, totalVestingDuration: 0, cliffDurationFromMigrationTime: 0, }, }, antiSniping: true, fee: { feeBps: 100, }, isLpLocked: true, tokenName: '', tokenSymbol: '', tokenImageContentType: 'image/jpeg', creator: wallet.publicKey.toBase58(), }, null, 2) }) ).json(); ``` ## Token Metadata The following 2 steps, are to upload your token image and metadata to the **static URL**, which will be the URI in the onchain metadata of your token. Example * URI/ Off-chain Metadata: `https://static-create.jup.ag/metadata/{mint}.json` * Image: `https://static-create.jup.ag/images/{mint}` You can refer to this to understand Token Metadata on Solana: [https://developers.metaplex.com/token-metadata](https://developers.metaplex.com/token-metadata) ### Upload Image From the response of the `create-tx` endpoint, we will need the `imagePresignedUrl` to make a **`PUT` request** to the url provided, in order to upload the token image. ```js theme={null} const imageResponse = await fetch(createTransaction.imagePresignedUrl, { method: 'PUT', headers: { 'Content-Type': 'image/jpeg', // Adjust based on the image type passed in previously }, body: fs.readFileSync('./token.jpeg'), // Assuming the image file is located in the same folder }); ``` ### Upload Metadata From the response of the `create-tx` endpoint, we will need the `metadataPresignedUrl` to make a **`PUT` request** to the url provided, in order to upload the token metadata. ```js theme={null} const metadataResponse = await fetch(createTransaction.metadataPresignedUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: '', symbol: '', description: '', image: createTransaction.imageUrl, website: '', twitter: '', telegram: '', }, null, 2), }); ``` ## Submit Transaction After you have uploaded your token image and token metadata, you can proceed to signing and making a post request to the `submit` endpoint - this will allow Jupiter Studio to complete the transaction and submit it to the network on your behalf. **NOTE** * Do note that the endpoint expects the `requestBody`'s `content` to be in [`multipart/form-data` format](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects). * Ensure the file types and size of the image file is manageable. **NOTE** The `content` and `headerImage` refers to the Studio dedicated page's token description and header image of the page, they are not on-chain metadata. This is meant for you to customize the Studio dedicated page as you wish - to include lore, story or just a nice looking banner! The `content` and `headerImage` are stored off-chain for our frontend to ingest and display. [Do not confuse this with the uploading of token metadata, they are done separately.](#token-metadata) ```js theme={null} import { VersionedTransaction } from '@solana/web3.js'; import fs from 'fs'; const transaction = VersionedTransaction.deserialize(Buffer.from(createTransaction.transaction, 'base64')); transaction.sign([wallet]); const signedTransaction = Buffer.from(transaction.serialize()).toString('base64'); const formData = new FormData(); formData.append('transaction', signedTransaction); formData.append('owner', wallet.publicKey.toBase58()); formData.append('content', ''); formData.append( 'headerImage', new File( [fs.readFileSync('/Path/to/header.jpeg')], 'header.jpeg', { type: 'image/jpeg' }, ) ); const result = await ( await fetch ( 'https://lite-api.jup.ag/studio/v1/dbc-pool/submit', { method: 'POST', body: formData, }) ).json(); ``` # Studio API Source: https://dev.jup.ag/docs/studio/index Studio is built for culture architects who want: * Aggressive experimentation. * Tools for growth and alignment. * Collaborative and supportive vibe culture between Studio projects. ## About Studio is a powerful playground equipped with a suite of tools for creators. Each feature is strategic towards how creators might want to customize to fit their needs - like flexible bonding curves, custom vesting schedules, and selectable quote mints to encode your vision. **Features** * LP Fees: 50% before AND after graduation. * LP Locking: Optional 50% of the graduated LP unlocks after 1 year. * Vested Tokens: 0 - 80% of token supply, with optional vesting schedule and cliff. * Flexible parameters: Quote mint, Market cap bonding, etc. * Other helpful tools: Anti-sniper suite, Lp Locking. **Dedicated Studio Token Page** Apart from the strategic levers, start rallying your community with the dedicated Studio page with seamless content integration with jup.ag's token page. * Dedicated Studio page for each token. * Content from Studio shows up in jup.ag's token page. **READINGS** * Design intentions: [https://x.com/9yointern/status/1940431614103937517](https://x.com/9yointern/status/1940431614103937517) * Launch post: [https://x.com/jup\_studio/status/1940620377602011566](https://x.com/jup_studio/status/1940620377602011566) * General FAQ: [https://support.jup.ag/hc/en-us/categories/21148110700060-Studio](https://support.jup.ag/hc/en-us/categories/21148110700060-Studio) ## FAQ * In order for us to track and store your token information, header image or token description, you **must** send your signed transaction from the `create_tx` endpoint to the `submit` endpoint. * This will allow us to store your token into our database and reflect it as a Studio token on our frontend. * If you submit the transaction on your own or some other way, the token will not have a dedicated Studio page. * Those URLs are for you to upload your token's metadata and image to a static endpoint, which will be in the token's URI metadata onchain. * You are required to make a PUT request to those endpoints, [you can refer to this section on the usage](/docs/studio/create-token#token-metadata). * If you do not upload your token image and metadata to this endpoint, your token will not have any image/metadata reflected onchain. * Lite URL: `https://lite-api.jup.ag/studio/v1`: 100 requests per 5 minutes * Pro URL: `https://api.jup.ag/studio/v1`: 10 requests per 10 seconds (for all Tiers) # Add Fees To Swap Source: https://dev.jup.ag/docs/swap/add-fees-to-swap Walkthrough on how to add fees to the Legacy Swap API. **INFO** As of January 2025, when integrating the Legacy Swap API, you no longer need to use the Referral Program to set up a `referralAccount` and `referralTokenAccount` to collect fees from the swaps you provide to the end users. Simply, just pass in any valid token account as the `feeAccount` parameter in the Legacy Swap API. However, do note that **it is still applicable to the Trigger API**. **NOTE** You can still find information about the Referral Program. The Referral Program is an open source program by Jupiter to provide referral fees for integrators who are integrating Jupiter Swap and Jupiter Limit Order. You can check out the code [here](https://github.com/TeamRaccoons/referral) to gain a better understanding of how it works. ## Use Case By default, there are **zero** protocol fees on Jupiter Swap. Integrators have the option to introduce a platform fee denoted in basis points, e.g. **20 bps** for **0.2%** of the token input or output. ### Important Notes * **Input mint or the output mint** on the swap for ExactIn. * **Input mint ONLY** on the swap for ExactOut. * Example, if you swap JUP to USDC, you cannot take fees in SOL, it has to be part of the swap. * It does not support Token2022 tokens. * Referral Program is no longer required. **Important Notes** * The Jupiter Swap project account for the Referral Program is `45ruCyfdRkWpRNGEqWzjCiXRHkZs8WXCLQ67Pnpye7Hp`. * The `referralTokenAccount` can either be: * **Input mint or the output mint** on the swap for ExactIn. * **Input mint ONLY** on the swap for ExactOut. * You can use the [Dashboard](https://referral.jup.ag/dashboard), [SDK](https://github.com/TeamRaccoons/referral/blob/main/example/src/createReferralAccount.ts) or [API](https://referral.jup.ag/api) to set up the `referralAccount` and `referralTokenAccount` in this guide. **Let’s Get Started** **1. Set up** You will need to complete the prerequisites and understanding of [Environment Setup](/get-started/environment-setup) and [Get Quote and Swap](/docs/swap/get-quote) guide as this is reliant on the Legacy Swap API. **Obtain `referralAccount` and `referralTokenAccount`** There are 3 ways you can set up a referral account. 1. Use our [referral dashboard](https://referral.jup.ag/dashboard) to create them. After creating, remember to find your `Referral Key` on the page and the associated token accounts. 2. Use our SDK to create them. You can use the [example scripts](https://github.com/TeamRaccoons/referral/tree/main/example/src) to create. 3. Use our API to create them. You can use this [API reference](https://referral.jup.ag/api) to create. **Obtain `mintAccount`** As for the mint account, assuming you have an interface where a user swaps, you will know up front what are the input or output mints. For the sake of example, we will use a hardcoded mint public key. ```js theme={null} const referralAccount = new Publickey('ReplaceWithPubkey'); const mintAccount = new Publickey('So11111111111111111111111111111111111111112'); ``` **2. Set your referral fee in Quote** Setting your referral fee is simple, just add `platformFeeBps` parameter to the `/quote` endpoint. In this example, we set `platformFeeBps` to `20` which equates to 0.2%. ```js theme={null} const quoteResponse = await ( await fetch( 'https://lite-api.jup.ag/swap/v1/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000&slippageBps=50&restrictIntermediateTokens=true&platformFeeBps=20' ) ).json(); console.log(JSON.stringify(quoteResponse, null, 2)); ``` **3. Set your referral token account in Swap** In order to refer and receive fees from all types of tokens, you will need to have already initialize `referralTokenAccount`s (owned by your `referralAccount`) for the mint in the swap. By calling the Swap API with the parameter `feeAccount`, which is the `referralTokenAccount`, you will receive the serialized swap transaction that will set a fee to be taken from the referred and sent to that token account. In this code block, we will be using the SDK to try to find the `referralTokenAccount` based on our previously defined `referralAccount` and `mintAccount`. * If the token account is found, it will proceed to the Legacy Swap API. * If the token account is not found, it will send a transaction to the network to attempt to initialize one for the mint. **Do note that transactions may fail due to various reasons like Priority Fees.** ```js theme={null} import { ReferralProvider } from "@jup-ag/referral-sdk"; const { tx, referralTokenAccountPubKey } = await provider.initializeReferralTokenAccount({ payerPubKey: wallet.publicKey, referralAccountPubKey: referralAccount, mint: mintAccount, }); const referralTokenAccount = await connection.getAccountInfo(referralTokenAccountPubKey); // Attempt to initialize a token account if (!referralTokenAccount) { const signature = await sendAndConfirmTransaction(connection, tx, [wallet]); console.log({ signature, referralTokenAccountPubKey: referralTokenAccountPubKey.toBase58() }); // Since initialized, it will carry on } else { console.log(`referralTokenAccount ${referralTokenAccountPubKey.toBase58()} for mint ${mintAccount.toBase58()} already exists`); }; const feeAccount = referralTokenAccountPubKey; console.log(feeAccount); ``` However, if you are confident that the `referralTokenAccount` for specific mints have been created, you can use this method to get it. **Do note that, even if the token account is not intialized, it will return a pubkey as it is a Program Derived Address. [Read more here.](https://solana.com/docs/core/pda#findprogramaddress)** ```js theme={null} const [feeAccount] = PublicKey.findProgramAddressSync( [ Buffer.from("referral_ata"), // A string that signifies the account type, here "referral_ata." referralAccount.toBuffer(), // The public key of the referral account converted into a buffer. mintAccount.toBuffer(), // The mint public key, converted into a buffer. ], new PublicKey("REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3") // The public key of the Referral Program ); ``` Using the above, we will now know the `feeAccount` to be passed in as the parameter in Legacy Swap API. You can refer to the [Build Swap Transaction](/docs/swap/build-swap-transaction) guide to add any parameters where necessary to help transaction sending, etc. ```js theme={null} const swapResponse = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey.toBase58(), // Pass in actual referred user in production feeAccount: feeAccount, }) }) ).json(); ``` ### 1. Set up You will need to complete the prerequisites and understanding of [Environment Setup](/get-started/environment-setup) and [Get Quote and Swap](/docs/swap/get-quote) guide as this is reliant on the Legacy Swap API. ### 2. Set your fee in Quote Setting your fee is simple, just add `platformFeeBps` parameter to the `/quote` endpoint. In this example, we set `platformFeeBps` to `20` which equates to 0.2%. ```js theme={null} const quoteResponse = await ( await fetch( 'https://lite-api.jup.ag/swap/v1/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000&slippageBps=50&restrictIntermediateTokens=true&platformFeeBps=20' ) ).json(); ``` ### 3. Set your feeAccount in Swap In the `/swap` endpoint, you will need to pass in the `feeAccount` parameter. The `feeAccount` is any token account that will receive the fees from the swap. Do ensure that the token account is initialized and is the correct mint to receive the fees in. ```js theme={null} const swapResponse = await ( await fetch('https://api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, // Pass in actual referred user in production feeAccount: feeAccount, }) }) ).json(); ``` ### 4. Sign and send transaction Finally, the user can sign the transaction and it can be submitted to the network to be executed. You can refer to the [Send Swap Transaction](/docs/swap/send-swap-transaction) guide to complete this step. ### Create Token Account To create a token account, you can use the following code or refer to [Solana Cookbook](https://solana.com/developers/cookbook/tokens/create-token-account). * The code creates the transaction to create the token account and handles the transaction siging and sending. * If the token account already exists, it will not create and might throw an error such as `Provided owner is not allowed`. ```js theme={null} import { createAssociatedTokenAccount } from "@solana/spl-token"; const mintPubkey = new PublicKey( "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", ); let ata = await createAssociatedTokenAccount( connection, // connection wallet, // fee payer mintPubkey, // mint wallet.publicKey, // owner of the token account // confirmOptions, // if you need to skip simulation and send the transaction immediately // programId, // if you need to use a different token program id such as token-2022 // associatedTokenProgramId, // allowOwnerOffCurve, // if you need to allow the owner to be off curve ); console.log(`ATA: ${ata.toBase58()}`); ``` # Build Swap Transaction Source: https://dev.jup.ag/docs/swap/build-swap-transaction Swap via Metis by building a swap transaction using the Legacy Swap API. **NOTE** * Lite URL: `https://lite-api.jup.ag/swap/v1/swap` * Pro URL: `https://api.jup.ag/swap/v1/swap` To upgrade to Pro or understand our rate limiting, please refer to this section. The Legacy Swap API is one of the ways for you to interact with the Jupiter Swap Aggregator program. Before you send a transaction to the network, you will need to build the transaction that defines the instructions to execute and accounts to read/write to. It can be complex to handle this yourself, but good news! Most of our APIs and SDKs just handles it for you, so you get a response with the transaction to be prepared and sent to the network. **USE LEGACY SWAP API TO HANDLE IT FOR YOU OR...** If you are looking to interact with the Jupiter Swap Aggregator program in a different way, check out the other guides: **Swap Instructions** To compose with instructions and build your own transaction, [read how to use the `/swap-instructions` in this section](#build-your-own-transaction-with-instructions). **Flash Fill or Cross Program Invocation (CPI)** To interact with your own Solana program, [read how to use the **Flash Fill method** or **CPI** in this section](#build-your-own-transaction-with-flash-fill-or-cpi). ## Let’s Get Started In this guide, we will pick up from where [**Get Quote**](/docs/swap/get-quote) guide has left off. If you have not set up your environment to use the necessary libraries, the RPC connection to the network and successfully get a quote from the Quote API, please start at [Environment Setup](/get-started/environment-setup) or [get quote](/docs/swap/get-quote). **API REFERENCE** To fully utilize the Legacy Swap API, check out the [Legacy Swap API or Swap Instructions Reference](/api-reference/swap/swap). ## Legacy Swap API From the previous guide on getting a quote, now using the quote response and your wallet, you can receive a **serialized swap transaction** that needs to be prepared and signed before sending to the network. ## Get Serialized Transaction Using the root URL and parameters to pass in, it is as simple as the example code below! **OPTIMIZING FOR TRANSACTION LANDING IS SUPER SUPER IMPORTANT!** This code block includes additional parameters that our Legacy Swap API supports, such as estimating compute units, priority fees and slippage, to optimize for transaction landing. To understand how these parameters help, the next step, [Send Swap Transaction guide](/docs/swap/send-swap-transaction) will discuss them. ```js theme={null} const swapResponse = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, // ADDITIONAL PARAMETERS TO OPTIMIZE FOR TRANSACTION LANDING // See next guide to optimize for transaction landing dynamicComputeUnitLimit: true, dynamicSlippage: true, prioritizationFeeLamports: { priorityLevelWithMaxLamports: { maxLamports: 1000000, priorityLevel: "veryHigh" } } }) }) ).json(); console.log(swapResponse); ``` From the above example, you should see this response. ```js theme={null} { swapTransaction: 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAGDkS+3LuGTbs......+/oD9qb31dH6i0QZ2IHELXUX3Y1YeW79p9Stkqk12z4yvZFJiQ4GCQwLBwYQBgUEDggNTQ==', 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 } ``` ## What’s Next Now, you are able to get a quote and use our Legacy Swap API to build the swap transaction for you. Next steps is to proceed to prepare and sign the transaction and send the signed transaction to the network. ## Additional Resources ### Build Your Own Transaction With Instructions If you prefer to compose with instructions instead of the provided transaction that is returned from the `/swap` endpoint (like the above example). You can post to `/swap-instructions` instead, it takes the same parameters as the `/swap` endpoint but returns you the instructions rather than the serialized transaction. **NOTE** In some cases, you may add more accounts to the transaction, which may exceed the transaction size limits. To work around this, you can use the `maxAccounts` parameter in `/quote` endpoint to limit the number of accounts in the transaction. [Refer to the GET /quote's `maxAccounts` guide for more details.](/docs/swap/get-quote#max-accounts) Example code snippet of using `/swap-instruction` ```js theme={null} const instructions = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap-instructions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, }) }) ).json(); if (instructions.error) { throw new Error("Failed to get swap instructions: " + instructions.error); } const { tokenLedgerInstruction, // If you are using `useTokenLedger = true`. computeBudgetInstructions, // The necessary instructions to setup the compute budget. setupInstructions, // Setup missing ATA for the users. swapInstruction: swapInstructionPayload, // The actual swap instruction. cleanupInstruction, // Unwrap the SOL if `wrapAndUnwrapSol = true`. addressLookupTableAddresses, // The lookup table addresses that you can use if you are using versioned transaction. } = instructions; const deserializeInstruction = (instruction) => { return new TransactionInstruction({ programId: new PublicKey(instruction.programId), keys: instruction.accounts.map((key) => ({ pubkey: new PublicKey(key.pubkey), isSigner: key.isSigner, isWritable: key.isWritable, })), data: Buffer.from(instruction.data, "base64"), }); }; const getAddressLookupTableAccounts = async ( keys: string[] ): Promise => { const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo( keys.map((key) => new PublicKey(key)) ); return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => { const addressLookupTableAddress = keys[index]; if (accountInfo) { const addressLookupTableAccount = new AddressLookupTableAccount({ key: new PublicKey(addressLookupTableAddress), state: AddressLookupTableAccount.deserialize(accountInfo.data), }); acc.push(addressLookupTableAccount); } return acc; }, new Array()); }; const addressLookupTableAccounts: AddressLookupTableAccount[] = []; addressLookupTableAccounts.push( ...(await getAddressLookupTableAccounts(addressLookupTableAddresses)) ); const blockhash = (await connection.getLatestBlockhash()).blockhash; const messageV0 = new TransactionMessage({ payerKey: payerPublicKey, recentBlockhash: blockhash, instructions: [ // uncomment if needed: ...setupInstructions.map(deserializeInstruction), deserializeInstruction(swapInstructionPayload), // uncomment if needed: deserializeInstruction(cleanupInstruction), ], }).compileToV0Message(addressLookupTableAccounts); const transaction = new VersionedTransaction(messageV0); ``` ### Build Your Own Transaction With Flash Fill Or CPI If you prefer to interact with the Jupiter Swap Aggregator program with your own on-chain program. There are 2 ways to do it, typically on-chain program call **Cross Program Invocation (CPI)** to interact with each other, we also have another method called **Flash Fill** built by Jupiter (due to limitations of CPI in the past). **CPI IS NOW RECOMMENDED!** As of January 2025, Jupiter Swap via CPI is recommended for most users. [The `Loosen CPI restriction` feature has been deployed on Solana, you can read more here](https://github.com/solana-labs/solana/issues/26641). **WHY FLASH FILL?** With Jupiter's complex routing, best prices comes at a cost. It often means more compute resources and accounts are required as it would route across multiple DEXes in one transaction. Solana transactions are limited to 1232 bytes, Jupiter is using [Address Lookup Tables (ALTs)](https://docs.solana.com/developing/lookup-tables) to include more accounts in one transaction. However, the CPI method cannot use ALTs, which means when you add more accounts to a Jupiter Swap transaction, it will likely fail if it exceeds the transaction size limits. **Flash Fill allows the use of Versioned Transaction and ALTs**, hence, reducing the total accounts used for a Jupiter Swap transaction. **A CPI transaction will be composed of these instructions:** 1. Borrow enough SOL from the program to open a wSOL account that the program owns. 2. Swap X token from the user to wSOL on Jupiter via CPI. 3. Close the wSOL account and send it to the program. 4. The program then transfers the SOL back to the user. **Links and Resources:** * [https://github.com/jup-ag/jupiter-cpi-swap-example](https://github.com/jup-ag/jupiter-cpi-swap-example) * [https://github.com/jup-ag/sol-swap-cpi](https://github.com/jup-ag/sol-swap-cpi) **[jupiter-cpi](https://github.com/jup-ag/jupiter-cpi)** To ease integration via CPI, you may add the following crate  to your program. In cargo.toml ```toml theme={null} [dependencies] jupiter-cpi = { git = "https://github.com/jup-ag/jupiter-cpi", rev = "5eb8977" } ``` In your code ```rust theme={null} use jupiter_cpi; ... let signer_seeds: &[&[&[u8]]] = &[...]; // Pass accounts to context one-by-one and construct accounts here // Or in practise, it may be easier to use remaining_accounts // https://book.anchor-lang.com/anchor_in_depth/the_program_module.html let accounts = jupiter_cpi::cpi::accounts::SharedAccountsRoute { token_program: , program_authority: , user_transfer_authority: , source_token_account: , program_source_token_account: , program_destination_token_account: , destination_token_account: , source_mint: , destination_mint: , platform_fee_account: , token_2022_program: , }; let cpi_ctx = CpiContext::new_with_signer( ctx.accounts.jup.to_account_info(), accounts, signer_seeds, ); jupiter_cpi::cpi::shared_accounts_route( cpi_ctx, id, route_plan, in_amount, quoted_out_amount, slippage_bps, platform_fee_bps, ); ... ``` **A Flash Fill transaction will be composed of these instructions:** 1. Borrow enough SOL for opening the wSOL account from this program. 2. Create the wSOL account for the borrower. 3. Swap X token to wSOL. 4. Close the wSOL account and send it to the borrower. 5. Repay the SOL for opening the wSOL account back to this program. **Links and resources:** * [https://github.com/jup-ag/sol-swap-flash-fill](https://github.com/jup-ag/sol-swap-flash-fill) # Common Errors Source: https://dev.jup.ag/docs/swap/common-errors List of errors that can be returned by the Jupiter Legacy Swap API, Swap Program or from other programs like DEXes, System or Token programs. In this section, you can find the list of errors that can be returned by the Jupiter Legacy Swap API, Swap Program or from other programs like DEXes, System or Token programs. ## Program Errors ### Jupiter Swap Program Errors **JUPITER SWAP PROGRAM IDL** You can find the full Swap Program IDL here: [https://solscan.io/account/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4#anchorProgramIdl](https://solscan.io/account/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4#anchorProgramIdl) **ABNORMAL ERROR RATES** If you face high or consistent amounts of errors, please reach out to [Jupiter Discord](https://discord.gg/jup). | Error Code | Error Name | Debug | | :--------- | :------------------------ | :-------------------------------------------------------------------------------------------------------------------- | | 6001 | SlippageToleranceExceeded | Try higher fixed slippage or try [`dynamicSlippage`](/docs/swap/send-swap-transaction#how-jupiter-estimates-slippage) | | 6008 | NotEnoughAccountKeys | Likely modified swap transaction causing missing account keys | | 6014 | IncorrectTokenProgramID | Likely attempted to take platform fees on a Token2022 token (This is also 0x177e) | | 6017 | ExactOutAmountNotMatched | Similar to slippage | | 6024 | InsufficientFunds | Insufficient funds for either swap amount, transaction fees or rent fees | | 6025 | InvalidTokenAccount | A token account passed in is invalid, it can be uninitialized or not expected | ### Solana Program Errors | Program | Link | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Token Program | [https://github.com/solana-program/token/blob/main/program/src/error.rs](https://github.com/solana-program/token/blob/main/program/src/error.rs) | | Token2022 Program | [https://github.com/solana-program/token-2022/blob/main/program/src/error.rs](https://github.com/solana-program/token-2022/blob/main/program/src/error.rs) | | Associated Token Account Program | [https://github.com/solana-program/associated-token-account/blob/main/program/src/error.rs](https://github.com/solana-program/associated-token-account/blob/main/program/src/error.rs) | | Other Solana Programs | [https://github.com/solana-program](https://github.com/solana-program) | ### DEX Program Errors In the swap transaction, the DEX in routing may return errors. You can find some of their IDLs and/or error codes in an explorer. If they do not support public IDLs or open source code, you can reference the common errors below or if you need additional help, please reach out to [Jupiter Discord](https://discord.gg/jup). | Error | Description | | :------------------------------------------------------ | :---------------------------------------------------------------------------------------------------- | | Error related to tick array or bitmap extension account | Similar to slippage, the price or market has "moved out of range", hence the swap transaction failed. | ## Routing Errors The common routing errors you may encounter are usually related to attempting to swap a token that is not tradable on Jupiter, for reasons such as lack of liquidity or the token is not supported. | Error | Description | Debug | | :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | NO\_ROUTES\_FOUND | No routes were found for the requested swap | \* Check jup.ag if it's routable
\* [Check the liquidity of the token's markets](https://support.jup.ag/hc/en-us/articles/18453861473436-Why-is-this-token-not-tradable-on-Jupiter) | | COULD\_NOT\_FIND\_ANY\_ROUTE | Unable to find any valid route for the swap | \* Check jup.ag if it's routable
\* [Check the liquidity of the token's markets](https://support.jup.ag/hc/en-us/articles/18453861473436-Why-is-this-token-not-tradable-on-Jupiter) | | ROUTE\_PLAN\_DOES\_NOT\_ CONSUME\_ALL\_THE\_AMOUNT | The calculated route cannot process the entire input amount, you can get more output amount by reducing your input amount | \* Try reducing your input amount | | MARKET\_NOT\_FOUND | The specified market address was not found | \* Verify the market address exists and is active | | TOKEN\_NOT\_TRADABLE | The specified token mint is not available for trading | \* Check jup.ag if it's routable
\* [Check the liquidity of the token's markets](https://support.jup.ag/hc/en-us/articles/18453861473436-Why-is-this-token-not-tradable-on-Jupiter) | | NOT\_SUPPORTED | Generic error for unsupported operations | \* Check the specific error message for details | | CIRCULAR\_ARBITRAGE\_ IS\_DISABLED | Attempted to swap a token for itself | \* Input and output tokens must be different | | CANNOT\_COMPUTE\_ OTHER\_AMOUNT\_THRESHOLD | Failed to calculate the minimum output amount based on slippage | \* Verify the input amount and slippage parameters are valid | ## Swap Transaction Composing Errors | Error | Description | Debug | | :------------------------------------------------------ | :------------------------------------------------------------ | :-------------------------------------------------------------- | | MAX\_ACCOUNT\_GREATER\_THAN\_MAX | The specified number of accounts exceeds the maximum allowed | \* Reduce the number of accounts in the transaction | | INVALID\_COMPUTE\_UNIT\_PRICE\_AND\_PRIORITIZATION\_FEE | Both compute unit price and prioritization fee were specified | - Use either compute unit price or prioritization fee, not both | | FAILED\_TO\_GET\_SWAP\_AND\_ACCOUNT\_METAS | Failed to generate the swap transaction | \* Check the error message for specific details | ## Best Practices It is important to understand the error codes when your products are user facing. This will help you provide a better experience for your users, helping them make an informed decision or follow up step to help their transaction succeed. **JUP.AG AS A REFERENCE** You can use [https://jup.ag/](https://jup.ag/) as a reference to understand how we handle errors on the UI. | Error Type | Best Practice | | :--------------------------- | :---------------------------------------------------------------------------------------------------- | | Slippage exceeding threshold | Show the user the current slippage tolerance and the incurred slippage | | Insufficient funds | Show the user the current balance of the account and the required balance | | Non Jupiter Program Errors | Allow the user to retry with a different route and/or exclude the specific DEX from the quote request | | Token not tradable | Show the user the token is not tradable and provide context on why it's not tradable | # Get Quote Source: https://dev.jup.ag/docs/swap/get-quote Swap via Metis by getting a quote from the Legacy Swap API. The Quote API enables you to tap into the Jupiter Metis v1 Routing Engine, which accesses the deep liquidity available within the DEXes of Solana's DeFi ecosystem. In this guide, we will walkthrough how you can get a quote for a specific token pair and other related parameters. **NOTE** * Lite URL: `https://lite-api.jup.ag/swap/v1/quote` * Pro URL: `https://api.jup.ag/swap/v1/quote` To upgrade to Pro or understand our rate limiting, please refer to this section. **PLEASE USE THE LEGACY SWAP API AT YOUR OWN DISCRETION.** The Jupiter UI at [https://jup.ag/](https://jup.ag/) contains multiple safeguards, warnings and default settings to guide our users to trade safer. Jupiter is not liable for losses incurred by users on other platforms. If you need clarification or support, please reach out to us in [Discord](https://discord.gg/jup). **ROUTING ENGINE** The quotes from Legacy Swap API are from the Jupiter Metis v1 Routing Engine. ## Let’s Get Started In this guide, we will be using the Solana web3.js package. If you have not set up your environment to use the necessary libraries and the connection to the Solana network, please head over to [Environment Setup](/get-started/environment-setup). **API REFERENCE** To fully utilize the Legacy Quote API, check out the [Legacy Quote API Reference](/api-reference/swap/quote). ## Legacy Quote API The most common trading pair on Solana is SOL and USDC, to get a quote for this specific token pair, you need to pass in the required parameters such as: | Parameters | Description | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | inputMint | The pubkey or token mint address e.g. So11111111111111111111111111111111111111112 | | outputMint | The pubkey or token mint address e.g. EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | | amount | The number of **input** tokens before the decimal is applied, also known as the “raw amount” or “integer amount” in lamports for SOL or atomic units for all other tokens. | | slippageBps | The number of basis points you can tolerate to lose during time of execution. e.g. 1% = 100bps | ## Get Quote Using the root URL and parameters to pass in, it is as simple as the example code below! ```js theme={null} const quoteResponse = await ( await fetch( 'https://lite-api.jup.ag/swap/v1/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000000&slippageBps=50&restrictIntermediateTokens=true' ) ).json(); console.log(JSON.stringify(quoteResponse, null, 2)); ``` Example response: ```json expandable theme={null} { "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", "feeAmount": "24825", "feeMint": "So11111111111111111111111111111111111111112" }, "percent": 100 } ], "contextSlot": 299283763, "timeTaken": 0.015257836 } ``` **TIP** `outAmount` refers to the best possible output amount based on the route at time of quote, this means that `slippageBps` does not affect. ## What’s Next Now, you are able to get a quote, next steps is to submit a transaction to execute the swap based on the quote given. Let’s go! ## Additional Resources ### Restrict Intermediate Tokens `restrictIntermediateTokens` can be set to `true` . If your route is routed through random intermediate tokens, it will fail more frequently. With this, we make sure that your route is only routed through highly liquid intermediate tokens to give you the best price and more stable route. ### Legacy Transactions All Jupiter swaps are using Versioned Transactions and [Address Lookup Tables](https://docs.solana.com/developing/lookup-tables). However, not all wallets support Versioned Transactions yet, so if you detect a wallet that does not support versioned transactions, you will need to set the `asLegacyTransaction` parameter to `true`. ### Adding Fees By using the Quote API in your app, you can add a fee to charge your users. You can refer to the `platformFeeBps` parameter and to add it to your quote and in conjuction, add `feeAccount` (it can be any valid token account) to your swap request. ### Direct Routes In some cases, you may want to restrict the routing to only go through 1 market. You can use the `onlyDirectRoutes` parameter to achieve this. This will ensure routing will only go through 1 market. **NOTE** * If there are no direct routes, there will be no quote. * If there is only 1 market but it is illiquid, it will still return the route with the illiquid market. **UNFAVORABLE TRADES** Please be aware that using `onlyDirectRoutes` can often yield unfavorable trades or outcomes. ### Max Accounts In some cases, you may want to add more accounts to the transaction for specific use cases, but it might exceed the transaction size limit. You can use the `maxAccounts` parameter to limit the number of accounts in the transaction. **UNFAVORABLE TRADES** Please be aware that the misuse of `maxAccounts` can yield unfavorable trades or outcomes. **TIP** Refer to the [Requote with Lower Max Accounts](/docs/swap/requote-with-lower-max-accounts) guide for more information on how to requote and adjust the swap when using `maxAccounts`. **NOTE** * `maxAccounts` is an estimation and the actual number of accounts may vary. * `maxAccounts` only applies to the total number of accounts of the inner swaps in the swap instruction and not any of the setup, cleanup or other instructions (see the example below). * We recommend setting `maxAccounts` to 64 * Keep `maxAccounts` as large as possible, only reduce `maxAccounts` if you exceed the transaction size limit. * If `maxAccounts` is set too low, example to 30, the computed route may drop DEXes/AMMs like Meteora DLMM that require more than 30 accounts. **Jupiter has 2 types of routing instructions**, if you plan to limit `maxAccounts`, you will need to account for if the market is routable with [ALTs](https://docs.solana.com/developing/lookup-tables) or not: * **`Routing Instruction`** (Simple Routing): The market is still new, and we do not have ALTs set up for the market, hence the number of accounts required is higher as there are more accounts required. * **`Shared Accounts Routing Instruction`**: The market has sufficient liquidity (and has been live for a while), and we have [ALTs](https://docs.solana.com/developing/lookup-tables) set up for the market to be used in the routing instruction, hence the number of accounts required is lower as there are less accounts required. [In this transaction](https://solscan.io/tx/2xpiniSn5z61hE6gB6EUaeRZCqeg8rLBEbiSnAjSD28tjVTSpBogSLfrMRaJiDzuqDyZ8v49Z7WL2TKvGQVwYbB7): Max Accounts Stabble Example Max Accounts Lifinity V2 Example Max Accounts Shared Accounts Route Example * You can see that there are a total of 2 inner swaps where the number of accounts respectively are * Stabble Stable Swap: 12 * Lifinity Swap V2: 13 * Total: 25 * The `maxAccounts` parameter is to control this value - to limit the total number of accounts in the inner swaps. * It doesn’t take into the consideration of a few things: * Each of the inner swap's program address, so 2 in this case. * Top level routing instruction accounts where in this case Shared Accounts Route is 13 and Route is 9. * There are also other accounts that are required to set up, clean up, etc which are not counted in the `maxAccounts` parameter Notes: * Values in the table are only estimations and the actual number of accounts may vary. * Min accounts are needed when we have already created the necessary [ALTs](https://docs.solana.com/developing/lookup-tables) for a specific pool resulting in less accounts needed in a Shared Accounts Routing context. * Sanctum and Sanctum Infinity are unique, and their accounts are dynamic. | DEX | Max | Min | | :-------------------- | :-- | :-- | | Meteora DLMM | 47 | 19 | | Meteora | 45 | 18 | | Moonshot | 37 | 15 | | Obric | 30 | 12 | | Orca Whirlpool | 30 | 12 | | Pumpfun AMM | 42 | 17 | | Pumpfun Bonding Curve | 40 | 16 | | Raydium | 45 | 18 | | Raydium CLMM | 45 | 19 | | Raydium CPMM | 37 | 14 | | Sanctum | 80 | 80 | | Sanctum Infinity | 80 | 80 | | Solfi | 22 | 9 | # About Legacy Swap API Source: https://dev.jup.ag/docs/swap/index The Jupiter Legacy Swap API enables you to tap into the Jupiter Metis v1 Routing Engine, which aggregates across all liquidity available within the DEXes of Solana's DeFi ecosystem, allowing you to swap seamlessly from any token to any token. ## We recommend using Ultra Swap API Ultra Swap API is the spiritual successor to Swap API, and is much simpler to use than Swap API. If you are first starting out on your Solana development journey, using Ultra Swap API is highly recommended over Swap API. For more information about Ultra Swap API, please refer to the [Ultra Swap API](/docs/ultra) documentation. **Using Legacy Swap API** Requires your own maintenance, optimizations and dependencies. * **Upkeep of RPCs**: To retrieve wallet balances, broadcast and retrieve transactions, etc. * **Deciding transaction fee**: Including, but not limited to, priority fee, Jito fee, etc. * **Deciding slippage**: The optimal slippage to use to balance between trade success and price protection, do note that [RTSE is only available via Ultra Swap API](/docs/ultra#real-time-slippage-estimator). * **Broadcasting the transaction**: Ultra uses a proprietary transaction sending engine which dramatically improves landing rate and speed. * **Parsing the swap results**: Polling and parsing the resulting transaction from the RPC, including handling for success and error cases. Though comes with other ways to use Jupiter. * Add custom instructions. * Use via Cross Program Invocation (CPI) calls. * Choose your own transactionbroadcasting method (like Typical RPCs, Jito, etc). * Modify the number of accounts to use in a transaction. ## Getting Started with Legacy Swap API 1. [**Get Quote**](/docs/swap/get-quote): Request for a quote which consists of the route plan, and other params such as integrator fee, slippage, etc. 2. [**Build Swap Transaction**](/docs/swap/build-swap-transaction): Post the quote to build a swap transaction. * You can utilize other methods to return swap instructions or use CPI rather than the default swap transaction. * You can utilize other parameters such as priority fee, dynamic slippage, etc to customize the transaction. 3. [**Send Swap Transaction**](/docs/swap/send-swap-transaction): Sign and send the swap transaction to the network via your preferred RPC or other methods. **Other Guides** * [**Adding Fees to Legacy Swap API**](/docs/swap/add-fees-to-swap): Add custom integrator fees to the swap transaction. * [**Using Legacy Swap API as a payment method**](/docs/swap/payments-through-swap): Use Legacy Swap API as a payment method for your users. # Payments Through Swap Source: https://dev.jup.ag/docs/swap/payments-through-swap Walkthrough on how to use the Legacy Swap API as a form of payment method. The Jupiter Legacy Swap API can be utilized such that you, a merchant can allow your customer to pay in any tokens while you still receive in your preferred token payment at the end of the transaction. ## Use Case Let’s set the stage. You are selling a **jupcake!!!** to your customer and merchant might only accept in 1 USDC, but your customer only has 1 SOL. Well, you’re at the right place! By using the Legacy Swap API, merchant can let customer pay in SOL while merchant still receive USDC in order to complete the payment for a jupcake. * Customer has 1,000,000 SOL. * Merchant sells 1 jupcake for 1 USDC. * Use the Legacy Swap API to swap exactly 1 USDC output from Customer's SOL. * Merchant receives the 1 USDC, as planned! ## Let’s Get Started ### 1. Setup You will need slightly different imports and also remember to set up connection to an RPC. If you have not set up the other typical libraries or are familiar with the Legacy Swap API, please follow this [Environment Setup](/get-started/environment-setup) and [Get Quote and Swap](/docs/swap/get-quote) guide. ```bash theme={null} npm i @solana/spl-token ``` ```js theme={null} import { PublicKey, Connection, Keypair, VersionedTransaction } from '@solana/web3.js'; import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token'; ``` Before we start getting a quote and swap transaction, for example sake, we will need to prepare both merchant and customer accounts. In production scenario, you will need to dynamically pass this in and allow users to sign in their device interfaces. **NOTE** Do note that you will need to have already set up: * **A wallet in your machine to simulate yourself as the customer as the customer is the signer of the transaction** (similar to how we set up in [Environment Setup](/get-started/environment-setup)). * `trackingAccount` is an additional Solana Account you can pass in to track only Jupiter transactions easily. #### Set Up Accounts ```js theme={null} const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const customerWallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // Your preferred token payment const customerAccount = customerWallet.publicKey; const merchantAccount = new PublicKey('ReplaceWithMerchantPubkey'); // const trackingAccount = new PublicKey('ReplaceWithPubkey'); // If required console.log("USDC_MINT:", USDC_MINT.publicKey); console.log("merchantAccount:", merchantAccount.publicKey); // console.log("trackingAccount:", trackingAccount.publicKey); ``` #### Set Up `destinationTokenAccount` One more thing you will need to set up! Later on, you will need to pass in `destinationTokenAccount` which will be your token account for your preferred token payment mint. **Do note that it is the merchant's token account and it needs to be initialized.** ```js theme={null} // Get the associated token account for the merchant wallet const merchantUSDCTokenAccount = await getAssociatedTokenAddress( USDC_MINT, merchantAccount, true, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID ); console.log("merchantUSDCTokenAccount:", merchantUSDCTokenAccount.publicKey); ``` ### 2. Set `swapMode` to `ExactOut` in Quote Next, the merchant have to [Get Quote](/docs/swap/get-quote) for the customer. We are using the `ExactOut` mode because we know exactly how much output amount (1 USDC) the merchant want to receive but not sure how much input amount the customer should pay with. By getting a quote first, the customer can know upfront the specific amount of input token before they approve and sign the transaction. **LIMITATIONS OF `ExactOut`** Currently, there are some limitations as `ExactOut` is not widely supported across all DEXes. * Supported DEXes are only Orca Whirlpool, Raydium CLMM, and Raydium CPMM. * NOT ALL token pairs may be available. ```js theme={null} const quoteResponse = await ( await fetch( 'https://lite-api.jup.ag/swap/v1/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000&slippageBps=50&restrictIntermediateTokens=true&swapMode=ExactOut' ) ).json(); console.log(JSON.stringify(quoteResponse, null, 2)); ``` From the this quote, you should get part of the response like this, where `amount` specified in the query parameter represents the `outAmount` in the response and of course, `swapMode: ExactOut`. ```js theme={null} { "inputMint": "So11111111111111111111111111111111111111112", "inAmount": "4434914", "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outAmount": "1000000", "otherAmountThreshold": "4434914", "swapMode": "ExactOut", ... } ``` ### 3. Set `destinationTokenAccount` in Swap The merchant then retrieves the serialized swap transaction, but the merchant need to specify the `destinationTokenAccount` in the parameters — this will build the swap transaction to swap but send to the [merchant's specified token account which we defined earlier](#set-up-destinationtokenaccount). The `destinationTokenAccount` should be the merchant’s token account to receive the payment in. Also do note that `customerAccount` should be accounted for. **You can refer to the [Build Swap Transaction](/docs/swap/build-swap-transaction) guide for other parameters to be passed in.** ```js theme={null} const swapResponse = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: customerAccount.publicKey, destinationTokenAccount: merchantUSDCTokenAccount.publicKey, // trackingAccount: trackingAccount.publicKey, }) }) ).json(); ``` ### 4. Prepare Transaction We have walked through the steps here and explained some of the code, you can refer to [Send Swap Transaction - Prepare Transaction](/docs/swap/send-swap-transaction#prepare-transaction). The main difference for payments is to ensure that the customer is the fee payer (the merchant can be generous and be the fee payer too!) and the signer. ```js theme={null} const transactionBase64 = swapResponse.swapTransaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); transaction.feePayer = customerAccount.publicKey; transaction.sign([customerWallet]); const transactionBinary = transaction.serialize(); ``` ### 5. Send Transaction We have walked through the steps here and explained some of the code, you can refer to [Send Swap Transaction - Send Transaction](/docs/swap/send-swap-transaction#send-transaction). The main difference for payments is, you might want to try adjusting `maxRetries` to a higher count as it is not time sensitive and ideally this is used with tighter slippage and ensuring the `inputMint` is not too unstable. Do note that more retries will cause the user to wait slightly longer, so find the balance between the two. Read more here: [https://solana.com/docs/advanced/retry](https://solana.com/docs/advanced/retry). ```js theme={null} const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 10, }); const confirmation = await connection.confirmTransaction({ signature }, "finalized"); if (confirmation.value.err) { throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}\nhttps://solscan.io/${signature}/`); } else console.log(`Transaction successful: https://solscan.io/tx/${signature}/`); ``` The succeeded Swap Transaction should show: * Token A swaps from the customer's token account * Token A swap to Token B * Token B sends to the merchant's token account # Requote with Lower Max Accounts Source: https://dev.jup.ag/docs/swap/requote-with-lower-max-accounts Walkthrough on how to requote with lower max accounts via the Legacy Swap API. In some cases where you might be limited or require strict control by adding your own instructions to the swap transaction, you might face issues with exceeding transaction size limit. In this section, we will provide some helping code to help you requote when the transaction size is too large. **NOTE** We provide a `maxAccounts` param in the `/quote` endpoint to allow you to reduce the total number of accounts used for a swap - this will allow you to add your own instructions. ## Example Code 1. Request for quote and the swap transaction as per normal. 2. Serialize the transaction. 3. Use the conditions to check if the transaction is too large. 1. If too large, requote again with lower max accounts - do note that the route will change. 2. If not, sign and send to the network. **TIP** We recommend `maxAccounts` 64 and start as high as you can, then incrementally reduce when requoting. Do note that with lower max accounts, it will might yield bad routes or no route at all. **TIP** When you serialize the transaction, you can log the number of raw bytes being used in the transaction. You can either add your custom instructions before or after serializing the transaction. ```js expandable theme={null} import { AddressLookupTableAccount, Connection, Keypair, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from '@solana/web3.js'; // Set up dev environment import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/key', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const connection = new Connection('your-own-rpc'); // Recommended const MAX_ACCOUNTS = 64 async function getQuote(maxAccounts) { const params = new URLSearchParams({ inputMint: 'insert-mint', outputMint: 'insert-mint', amount: '1000000', slippageBps: '100', maxAccounts: maxAccounts.toString() }); const url = `https://lite-api.jup.ag/swap/v1/quote?${params}`; const response = await fetch(url); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status}: ${errorText}`); } const quoteResponse = await response.json(); if (quoteResponse.error) { throw new Error(`Jupiter API error: ${quoteResponse.error}`); } return quoteResponse; }; async function getSwapInstructions(quoteResponse) { const response = await fetch('https://lite-api.jup.ag/swap/v1/swap-instructions', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ quoteResponse: quoteResponse, userPublicKey: wallet.publicKey.toString(), prioritizationFeeLamports: { priorityLevelWithMaxLamports: { maxLamports: 10000000, priorityLevel: "veryHigh" } }, dynamicComputeUnitLimit: true, }, null, 2) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status}: ${errorText}`); } const swapInstructionsResponse = await response.json(); if (swapInstructionsResponse.error) { throw new Error(`Jupiter API error: ${swapInstructionsResponse.error}`); } return swapInstructionsResponse; }; async function buildSwapTransaction(swapInstructionsResponse) { const { computeBudgetInstructions, setupInstructions, swapInstruction, cleanupInstruction, addressLookupTableAddresses, } = swapInstructionsResponse; const deserializeInstruction = (instruction) => { if (!instruction) return null; return new TransactionInstruction({ programId: new PublicKey(instruction.programId), keys: instruction.accounts.map((key) => ({ pubkey: new PublicKey(key.pubkey), isSigner: key.isSigner, isWritable: key.isWritable, })), data: Buffer.from(instruction.data, "base64"), }); }; const getAddressLookupTableAccounts = async ( keys ) => { const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo( keys.map((key) => new PublicKey(key)) ); return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => { const addressLookupTableAddress = keys[index]; if (accountInfo) { const addressLookupTableAccount = new AddressLookupTableAccount({ key: new PublicKey(addressLookupTableAddress), state: AddressLookupTableAccount.deserialize(accountInfo.data), }); acc.push(addressLookupTableAccount); } return acc; }, []); }; const addressLookupTableAccounts = []; addressLookupTableAccounts.push( ...(await getAddressLookupTableAccounts(addressLookupTableAddresses)) ); const blockhash = (await connection.getLatestBlockhash()).blockhash; // Create transaction message with all instructions const messageV0 = new TransactionMessage({ payerKey: wallet.publicKey, recentBlockhash: blockhash, instructions: [ ...(computeBudgetInstructions?.map(deserializeInstruction).filter(Boolean) || []), ...(setupInstructions?.map(deserializeInstruction).filter(Boolean) || []), deserializeInstruction(swapInstruction), ...(cleanupInstruction ? [deserializeInstruction(cleanupInstruction)].filter(Boolean) : []), ].filter(Boolean), }).compileToV0Message(addressLookupTableAccounts); const transaction = new VersionedTransaction(messageV0); return transaction; } async function checkTransactionSize(transaction) { // Max raw bytes of a Solana transaction is 1232 raw bytes // Using the conditions below, we can check the size of the transaction // (or if it is too large to even serialize) try { const transactionUint8Array = transaction.serialize(); console.log(transactionUint8Array.length) // Use 1232 assuming you have added your instructions to the transaction above // If you have not add your instructions, you will need to know how much bytes you might use return (transactionUint8Array.length > 1232); } catch (error) { if (error instanceof RangeError) { console.log("Transaction is too large to even serialize (RangeError)"); return true; } else { throw error; // Re-throw if it's not a RangeError } } } // Main execution logic with retry mechanism let counter = 0; let transactionTooLarge = true; let quoteResponse, swapInstructionsResponse, transaction; while (transactionTooLarge && counter < MAX_ACCOUNTS) { try { console.log(`Attempting with maxAccounts: ${MAX_ACCOUNTS - counter}`); quoteResponse = await getQuote(MAX_ACCOUNTS - counter); swapInstructionsResponse = await getSwapInstructions(quoteResponse); transaction = await buildSwapTransaction(swapInstructionsResponse); transactionTooLarge = await checkTransactionSize(transaction); if (transactionTooLarge) { console.log(`Transaction too large (with ${MAX_ACCOUNTS - counter} maxAccounts), retrying with fewer accounts...`); counter++; } else { console.log(`Transaction size OK with ${MAX_ACCOUNTS - counter} maxAccounts`); } } catch (error) { console.error('Error in attempt:', error); counter += 2; // Incrementing by 1 account each time will be time consuming, you can use a higher counter transactionTooLarge = true; } } if (transactionTooLarge) { console.error('Failed to create transaction within size limits after all attempts'); } else { console.log('Success! Transaction is ready for signing and sending'); // After, you can add your transaction signing and sending logic } ``` ## Example Response ```bash theme={null} Attempting with maxAccounts: 64 Transaction is too large to even serialize (RangeError) Transaction too large (with 64 maxAccounts), retrying with fewer accounts... Attempting with maxAccounts: 63 Transaction is too large to even serialize (RangeError) Transaction too large (with 63 maxAccounts), retrying with fewer accounts... ... Attempting with maxAccounts: 57 1244 Transaction too large (with 57 maxAccounts), retrying with fewer accounts... Attempting with maxAccounts: 56 1244 Transaction too large (with 56 maxAccounts), retrying with fewer accounts... ... Attempting with maxAccounts: 51 1213 Transaction size OK with 51 maxAccounts Success! Transaction is ready for signing and sending ``` # Send Swap Transaction Source: https://dev.jup.ag/docs/swap/send-swap-transaction Swap via Metis by sending a swap transaction to the network. Transaction sending can be very simple but optimizing for transaction landing can be challenging. This is critical in periods of network congestion when many users and especially bots are competing for block space to have their transactions processed. **IMPROVE TRANSACTION LANDING TIP** By using Jupiter Legacy Swap API, you can enable Dynamic Slippage, Priority Fee estimation and Compute Unit estimation, all supported on our backend and served directly to you through our API. ## Let’s Get Started In this guide, we will pick up from where [**Get Quote**](/docs/swap/get-quote) and [**Build Swap Transaction**](/docs/swap/build-swap-transaction) guide has left off. If you have not set up your environment to use the necessary libraries, the RPC connection to the network and successfully get a quote from the Quote API, please start at [Environment Setup](/get-started/environment-setup) or [get quote](/docs/swap/get-quote). ## Prepare Transaction **WHO IS THE SIGNER?** The most important part of this step is to sign the transaction. For the sake of the guide, you will be using the file system wallet you have set up to sign and send yourself. However, for other production scenarios such as building your own program or app on top of the Legacy Swap API, you will need the user to be the signer which is often through a third party wallet provider, so do account for it. In the previous guide, we are able to get the `swapTransaction` from the Legacy Swap API response. However, you will need to reformat it to sign and send the transaction, here are the formats to note of. | Formats | Description | | :--------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | | Serialized Uint8array format | The correct format to send to the network. | | Serialized base64 format | This is a text encoding of the Uint8array data, meant for transport like our Legacy Swap API or storage. You should not sign this directly. | | Deserialized format | This is the human-readable, object-like format before serialization. This is the state you will sign the transaction. | Here's the code to deserialize and sign, then serialize. 1. `swapTransaction` from the Legacy Swap API is a serialized transaction in the **base64 format**. 2. Convert it to **Uint8array (binary buffer) format**. 3. Deserialize it to a **VersionedTransaction** object to sign. 4. Finally, convert it back to **Uint8array** format to send the transaction. ```js theme={null} const transactionBase64 = swapResponse.swapTransaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); console.log(transaction); transaction.sign([wallet]); const transactionBinary = transaction.serialize(); console.log(transactionBinary); ``` **BLOCKHASH VALIDITY** If you look at the response of `console.log(transaction);`, you can see that our backend has already handled the blockhash and last valid block height in your transaction. The validity of a blockhash typically lasts for 150 slots, but you can manipulate this to reduce the validity of a transaction, resulting in faster failures which could be useful in certain scenarios. ## Send Transaction ### Transaction Sending Options Finally, there are a 2 [transaction sending options](https://solana.com/docs/advanced/retry#an-in-depth-look-at-sendtransaction) that we should take note of. Depending on your use case, these options can make a big difference to you or your users. For example, if you are using the Legacy Swap API as a payment solution, setting higher `maxRetries` allows the transaction to have more retries as it is not as critical compared to a bot that needs to catch fast moving markets. | Options | Description | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [maxRetries](https://solana.com/docs/advanced/retry) | Maximum number of times for the RPC node to retry sending the transaction to the leader. If this parameter is not provided, the RPC node will retry the transaction until it is finalized or until the blockhash expires. | | [skipPreflight](https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight) | If true, skip the preflight transaction checks (default: false). - Verify that all signatures are valid. | * Check that the referenced blockhash is within the last 150 blocks. * Simulate the transaction against the bank slot specified by the preflightCommitment. | ```js theme={null} const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 2, skipPreflight: true }); ``` ### Transaction Confirmation In addition, after sending the transaction, it is always a best practice to check the transaction confirmation state, and if not, log the error for debugging or communicating with your users on your interface. ```js theme={null} 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}/`); ``` ## Swap Transaction Executed! If you have followed the guides step by step without missing a beat, your transaction *should* theoretically land and you can view the link in console log to see the [transaction](https://solscan.io/tx/zEWGsd5tSyxUdsTn27hUzaJBadQSiFxF2X1CxVdQzdtgc3BpqyDPf5VQCFUScidhHJP5PchY33oJ3tZJLK5KXrf). ## Oh? Transaction Not Landing? As the Solana network grew and increased in activity over the years, it has become more challenging to land transactions. There are several factors that can drastically affect the success of your transaction: * Setting competitive priority fee * Setting accurate amount of compute units * Managing slippage effectively * Broadcasting transaction efficiently * Other tips ### How Jupiter Estimates Priority Fee? You can pass in `prioritizationFeeLamports` to Legacy Swap API where our backend will estimate the Priority Fee for you. We are using [Triton’s `getRecentPrioritizationFees`](https://docs.triton.one/chains/solana/improved-priority-fees-api) to estimate using the local fee market in writable accounts of the transaction (comparing to the global fee market), across the past 20 slots and categorizing them into different percentiles. | Parameters | Description | | :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxLamports` | A maximum cap applied if the estimated priority fee is too high. This is helpful when you have users using your application and can be a safety measure to prevent overpaying. | | `global` | A boolean to choose between using a global or local fee market to estimate. If `global` is set to `false`, the estimation focuses on fees relevant to the **writable accounts** involved in the instruction. | | `priorityLevel` | A setting to choose between the different percentile levels. Higher percentile will have better transaction landing but also incur higher fees. - `medium`: 25th percentile | * `high`: 50th percentile * `veryHigh`: 75th percentile | ```js theme={null} const swapResponse = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, prioritizationFeeLamports: { priorityLevelWithMaxLamports: { maxLamports: 10000000, global: false, priorityLevel: "veryHigh" } } }) }) ).json(); ``` ### How Jupiter Estimates Compute Unit Limit? You can pass in `dynamicComputeUnitLimit` to Legacy Swap API where our backend will estimate the Compute Unit Limit for you. When `true`, it allows the transaction to utilize a dynamic compute unit rather than using incorrect compute units which can be detrimental to transaction prioritization. Additionally, the amount of compute unit used and the compute unit limit requested to be used are correlated to the amount of priority fees you pay. ```js theme={null} const swapTransaction = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, dynamicComputeUnitLimit: true }) }) ).json(); ``` ### How Jupiter Estimates Slippage? Slippage is an unavoidable aspect of trading on decentralized exchanges (DEXes). #### About Slippage * **Token Pair:** The same fixed slippage setting can have very different effects depending on the tokens involved. For example, swapping between two stablecoins is much less volatile than swapping between two meme coins. * **Timing:** The time between when you receive a quote and when you actually send the swap transaction matters. Any delay can result in the price moving outside your slippage threshold. * **Transaction Landing:** How efficiently your transaction lands on-chain also affects slippage. Poorly optimized transactions may experience more slippage. - If you use the Legacy Swap API: * You are limited to fixed and dynamic slippage (no longer maintained) settings. * You are responsible for handling slippage and optimizing transaction landing yourself. - [Alternatively, consider using the Ultra Swap API](/docs/ultra): * All of these optimizations are handled for you - without any RPC from you. * Additional routing is available to RFQ (Request for Quote) systems like Jupiterz where slippage is not an issue because the market maker fills your order exactly as quoted. #### Dynamic Slippage Apart from the fixed slippage setting, you can use Dynamic Slippage: During swap transaction building, we will simulate the transaction and estimate a slippage value, which we then factor in the token categories heuristics to get the final slippage value. **DYNAMIC SLIPPAGE VS REAL TIME SLIPPAGE ESTIMATOR (RTSE)** RTSE is very different from Dynamic Slippage and has provided a much better user experience and results. RTSE is able to intelligently estimate the best possible slippage to use at the time of execution, balancing between trade success and price protection. RTSE uses a variety of heuristics, algorithms and monitoring to ensure the best user experience: * **Heuristics**: Token categories, historical and real-time slippage data, and more. * **Algorithms**: Exponential Moving Average (EMA) on slippage data, and more. * **Monitoring**: Real-time monitoring of failure rates to ensure reactiveness to increase slippage when necessary. **WARNING** Do note that we have discontinued development on Dynamic Slippage. ```js theme={null} const swapTransaction = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, dynamicSlippage: true, }) }) ).json(); ``` ### How Jupiter Broadcast Transactions? Transaction broadcasting is the process of submitting a signed transaction to the network so that validators can verify, process, and include it in a block. #### Broadcasting Through RPCs After you’ve built and signed your transaction, the signed transaction is serialized into a binary format and sent to the network via a Solana RPC node. The RPC node will verify and relay the transaction to the leader validator responsible for producing the next block. This is the most typical method to send transactions to the network to get executed. It is simple but you need to make sure the transactions are: * Send in the serialized transaction format. * Use fresh blockhash and last valid blockheight. * Use optimal amount of priority fees and compute unit limit. * Free of error. * Utilize retries. * Configure your RPCs * Optional but you can send your transaction to a staked RPC endpoint also known as [Stake-Weighted Quality of Service (SWQoS)](https://solana.com/developers/guides/advanced/stake-weighted-qos). * Used dedicated RPC services versus free or shared, depending on how critical your usage is. * Propagate to multiple RPC rather than reliant on one. #### Broadcasting Through Jito To include Jito Tips in your Legacy Swap transaction, you can do specify in the Legacy Swap API parameters. However, please take note of these when sending your transaction to Jito and [you can find thsese information in their documentation](https://docs.jito.wtf/): * You need to submit to a Jito RPC endpoint for it to work. * You need to send an appropriate amount of Jito Tip to be included to be processed. **MORE ABOUT JITO** You can leverage [Jito](https://www.jito.wtf/) to send transactions via tips for faster inclusion and better outcomes. Similar to Priority Fees, Jito Tips incentivize the inclusion of transaction bundles during block production, enhancing users' chances of securing critical transactions in competitive scenarios. Additionally, Jito enables bundling transactions to ensure they execute together or not at all, helping protect against front-running and other MEV risks through “revert protection” if any part of the sequence fails, all while reducing transaction latency for timely execution. ```js theme={null} const swapTransaction = await ( await fetch('https://lite-api.jup.ag/swap/v1/swap', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quoteResponse, userPublicKey: wallet.publicKey, prioritizationFeeLamports: { jitoTipLamports: 1000000 // note that this is FIXED LAMPORTS not a max cap } }) }) ).json(); ``` # About Tokens Source: https://dev.jup.ag/docs/tokens/index Understand how tokens are listed and verified on Jupiter. The Jupiter Tokens API and verification system aims to provide a way to validate mint addresses and provide integrators a simply way to get mint information. **DEPRECATED** [Tokens API V1](/docs/tokens/v1) will be/is deprecated by 1 August 2025. Please migrate to [Tokens API V2](/docs/tokens/v2) which consists of breaking changes. ## About As Solana grew and exploded with tens of thousands of newly minted tokens a day, the Jupiter Tokens API and verification system has evolved to meet the demands of token verification and provide an ecosystem-wide source of truth to rely on. A historical breakdown of the evolutions of the Tokens API and verification system. * [Solana Token Registry](https://github.com/solana-labs/token-list) was deprecated in 2022. * [Ecosystem Token List V1: Github](https://github.com/jup-ag/token-list): Maintained via Github with 4.8k Pull Requests verified manually. * [Ecosystem Token List V2: Catdet List](https://catdetlist.jup.ag): Maintained by Catdets and community with simple metrics to aid review. * [Ecosystem Token List V3: Verify](https://verify.jup.ag): Using a variety of trading, social metrics and [Organic Score](/docs/tokens/organic-score) to aid verification. * [Jup.ag/verify](https://jup.ag/verify): A predecessor and evolved version of `verify.jup.ag`, where you can burn 1,000 JUP to get an express review. **MORE READING MATERIALS** * [Introducing a new token verification method](https://x.com/9yointern/status/1907425355071197347) at [https://verify.jup.ag](https://verify.jup.ag) * [Background and History of the Ecosystem Token List V2](https://www.jupresear.ch/t/ecosystem-master-token-list/19786) # Organic Score Source: https://dev.jup.ag/docs/tokens/organic-score Understand how Organic Score is derived and how it is used to measure the genuine activity and health of a token. Organic Score is a metric designed to measure the genuine activity and health of a token. Unlike traditional metrics that can be easily manipulated by artificial trading or bot activity, the Organic Score focuses on real user participation and authentic market metrics. This helps users, developers, and projects better understand the context of similar tokens and find the signal within the noise. ## How Organic Score is Derived Organic Score is derived from a set of core metrics, such as holder count, trading volume and liquidity. In order to ensure the authenticity and reliability of the score, we track the metrics participated by real user wallets (not bots, etc) in real time to derive the Organic Score. **ORGANIC SCORE** This is a high level depiction of how Organic Score is dervied, there are other heuristics and data involved to measure and derive it. Organic Score # Token Tag Standard Source: https://dev.jup.ag/docs/tokens/token-tag-standard Use the token tag standard to get your tokens tagged for better visibility on Jupiter UI or via the Tokens API. The Tokens API is built with a tagging system such as Verified, LSTs and more. In this section, we will be going through how you can get your tokens tagged for better visibility on Jupiter UI or via the Tokens API. ## Requirements * [An endpoint that points to a .csv file with a mint address per row](https://raw.githubusercontent.com/jup-ag/token-list/main/examples/sample_tags.csv). * A preferred word or acronym for your tag - one that's short and mobile friendly. * A set interval for us to poll. The endpoint should be public, with our IP whitelisted for rate limits where necessary. ## How to get your tokens tagged After you have completed the requirements, please reach out to us via [Discord](https://discord.gg/jup). Once we start ingesting your list, the tokens will be tagged automatically. # s API V1 (Deprecated) Source: https://dev.jup.ag/docs/tokens/v1 Guide on Tokens V1 API endpoints. The Tokens V1 API is deprecated and please use Tokens V2 API. **DEPRECATED** [Tokens API V1](/docs/tokens/v1) will be/is deprecated by 30th September 2025. Please migrate to [Tokens API V2](/docs/tokens/v2) which consists of breaking changes. **NOTE** Base URL: `https://lite-api.jup.ag/tokens/v1` For higher rate limits, [refer to the API Key Setup doc](/portal/setup). ## Get Token Information Using this endpoint, you can get the token information of the specific mint address. In the following example, we are looking at getting the token information of the JUP token. **USEFUL MINT INFORMATION** In the response, you can see that we have identified the `tags`, [`freeze_authority`](https://spl.solana.com/token#freezing-accounts) and [`permanent_delegate`](https://spl.solana.com/token#authority-delegation) to help you or your users make informed decisions. ```js theme={null} const tokenInfoResponse = await ( await fetch('https://lite-api.jup.ag/tokens/v1/token/JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN') ).json(); console.log(tokenInfoResponse); ``` From the above example, you should see this response. ```js theme={null} { address: 'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN', name: 'Jupiter', symbol: 'JUP', decimals: 6, logoURI: 'https://static.jup.ag/jup/icon.png', tags: [ 'verified', 'strict', 'community', 'birdeye-trending' ], daily_volume: 79535977.0513354, created_at: '2024-04-26T10:56:58.893768Z', freeze_authority: null, mint_authority: null, permanent_delegate: null, minted_at: '2024-01-25T08:54:23Z', extensions: { coingeckoId: 'jupiter-exchange-solana' } } ``` ## Get Tokens In Market Using this endpoint, you can get a list of token mints that belong to a market/pool address. In the following example, we use a [Meteora SOL-USDC market](https://solscan.io/account/BVRbyLjjfSBcoyiYFuxbgKYnWuiFaF9CSXEa5vdSZ9Hh). ```js theme={null} const marketTokensResponse = await ( await fetch('https://lite-api.jup.ag/tokens/v1/market/BVRbyLjjfSBcoyiYFuxbgKYnWuiFaF9CSXEa5vdSZ9Hh/mints') ).json(); console.log(marketTokensResponse); ``` From the above example, you should see this response. ```js theme={null} [ 'So11111111111111111111111111111111111111112', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' ] ``` ## Get All Tradable Tokens Using this endpoint, you can get a list of all token mints that are tradable on Jupiter. * A new token (before market liquidity checks) * Or tokens that has past the market liquidity checks * These tokens should return a quote from the `/quote` endpoint and is able to swap. ```js theme={null} const allTradableResponse = await ( await fetch('https://lite-api.jup.ag/tokens/v1/mints/tradable') ).json(); console.log(allTradableResponse); ``` From the above example, you should see this response. ```js theme={null} [ ... 'So11111111111111111111111111111111111111112', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' 'jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v', 'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN', '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4', ... ] ``` ## Get Tagged Tokens Using this endpoint, you can get a list of token mints (with information) that are tagged according to the tag you pass in. In the following example, we use the `lst` tag. **TIP** A list of useful tags are: | **Token List Name** | **Description** | | :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **verified** | A list of verified tokens, consisting of community-verified tokens via [catdetlist.jup.ag](https://catdetlist.jup.ag) and the previous standard of Jupiter Strict. | | **lst** | A list of liquid staked tokens, maintained with Sanctum. | | **token-2022** | A list of all token-2022 tokens. | You can pass in multiple tags using a comma separated list, refer to the API Reference for more details. ```js theme={null} const lstTaggedResponse = await ( await fetch('https://lite-api.jup.ag/tokens/v1/tagged/lst') ).json(); console.log(lstTaggedResponse); ``` From the above example, you should see this response. ```js theme={null} ... { address: 'jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v', name: 'Jupiter Staked SOL', symbol: 'JupSOL', decimals: 9, logoURI: 'https://static.jup.ag/jupSOL/icon.png', tags: [ 'verified', 'community', 'strict', 'lst' ], daily_volume: 24017778.687489692, created_at: '2024-04-26T10:57:45.759228Z', freeze_authority: null, mint_authority: 'EMjuABxELpYWYEwjkKmQKBNCwdaFAy4QYAs6W9bDQDNw', permanent_delegate: null, minted_at: '2024-03-25T09:28:04Z', extensions: { coingeckoId: 'jupiter-staked-sol' } }, ... ``` ## Get New Tokens Using this endpoint, you can get a list of token mints (with information) **sorted by `created_at` their timestamps**. **PAGINATE LARGE RESPONSE** The `/new` endpoint will return a large sized payload as response, you can utilize the `limit` and `offset` query parameters to help paginate the responses. * `limit`: Refers to how many counts of data to be in the output. * `offset`: Refers to how many counts of data to offset into the result set. * Used in conjunction with `limit` to page through the data. ```js theme={null} const newTokensReponse = await ( await fetch('https://lite-api.jup.ag/tokens/v1/new') ).json(); console.log(newTokensReponse); ``` From the above example, you should see this response. ```js theme={null} { mint: 'penguin', created_at: '1733481083', metadata_updated_at: 1733481087, name: 'cool penguin', symbol: 'penguin', decimals: 6, logo_uri: 'https://jup.ag', known_markets: [ 'market' ], mint_authority: null, freeze_authority: null }, { mint: 'cat', created_at: '1733481083', metadata_updated_at: 1733481087, name: 'cat moon', symbol: 'cat', decimals: 6, logo_uri: 'https://jup.ag', known_markets: [ 'market' ], mint_authority: null, freeze_authority: null }, ``` ## Get All Tokens Using the endpoint, you can simply query with the `all` resource to get all tokens that Jupiter has indexed through our infrastructure. **WARNING** Do note that calling this endpoint's resource will return **a large payload of 300+MB**, which would introduce some latency in the call. Please use carefully and intentionally, else utilize the other endpoints. This endpoint does not support `limit` or `offset`. **TIP** To index your own tokens, you can use RPC APIs such as the [Metaplex Digital Asset Standard (DAS)](https://developers.metaplex.com/das-api). Major RPC providers like [Helius](https://docs.helius.dev/compression-and-das-api/digital-asset-standard-das-api) and [Triton One](https://docs.triton.one/digital-assets-api/introduction) offer access to this API. ```js theme={null} const allResponse = await ( await fetch("https://lite-api.jup.ag/tokens/v1/all") ).json(); console.log(allResponse); ``` From the above example, you should see this response. ```js theme={null} ... { address: 'So11111111111111111111111111111111111111112', name: 'Wrapped SOL', symbol: 'SOL', decimals: 9, logoURI: 'https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png', tags: [ 'verified', 'community', 'strict' ], daily_volume: 2873455332.377303, created_at: '2024-04-26T10:56:58.893768Z', freeze_authority: null, mint_authority: null, permanent_delegate: null, minted_at: null, extensions: { coingeckoId: 'wrapped-solana' } }, { address: 'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN', name: 'Jupiter', symbol: 'JUP', decimals: 6, logoURI: 'https://static.jup.ag/jup/icon.png', tags: [ 'verified', 'strict', 'community', 'birdeye-trending' ], daily_volume: 79535977.0513354, created_at: '2024-04-26T10:56:58.893768Z', freeze_authority: null, mint_authority: null, permanent_delegate: null, minted_at: '2024-01-25T08:54:23Z', extensions: { coingeckoId: 'jupiter-exchange-solana' } }, ... ``` # Tokens API V2 (Beta) Source: https://dev.jup.ag/docs/tokens/v2 Guide on Tokens V2 API endpoints to query for mint information in specific searches, tags or categories. **NOTE** * Lite URL: `https://lite-api.jup.ag/tokens/v2` * Pro URL: `https://api.jup.ag/tokens/v2` To upgrade to Pro or understand our rate limiting, please refer to this section. **USEFUL MINT INFORMATION** * Token Metadata like name, symbol, icon to display token information to users * [Organic Score](/docs/tokens/organic-score), Holder count, Market cap, etc can be useful to help make a better trading decision * And much more! Do note that the response is subject to changes as we continue to improve. Refer to [Example Response](#example-response) or [Tokens API V2 Reference](/api-reference/tokens/v2) for full schema. ## Query by Mint The Tokens API V2 provides an endpoint to search tokens in the background for you and returns you the search results, along with the mint information. This is useful in most user applications, as users need to choose which tokens they want to swap. This also provides a seamless developer experience as integrating this allows us to handle and abstract the token search mechanism, allowing you to focus on other user features. **SEARCH** * Search for a token and its information by its **symbol, name or mint address**. * Comma-separate to search for multiple. * Limit to 100 mint addresses in query. * Default to 20 mints in response when searching via symbol or name. ```js theme={null} const searchResponse = await ( await fetch(`https://lite-api.jup.ag/tokens/v2/search?query=So11111111111111111111111111111111111111112`) ).json(); ``` ## Query by Tag The Tokens API V2 provides an endpoint to query by tags. This is useful to help users distinguish between verified vs non-verified or specific groups of tokens like liquid-staked tokens (LSTs). **TAGS** * Only `lst` or `verified` tag. * Note that this will return the entire array of existing mints that belongs to the tag. ```js theme={null} const tagResponse = await ( await fetch(`https://lite-api.jup.ag/tokens/v2/tag?query=verified`) ).json(); ``` ## Get Category The Tokens API V2 provides an endpoint to get mints and their mint information by categories. These categories are useful for identifying tokens in specific trading scenarios, providing users with more information to trade with. **CATEGORY** * Only `toporganicscore`, `toptraded` or `toptrending` category. * Added query by interval for more accuracy, using `5m`, `1h`, `6h`, `24h`. * The result filters out generic top tokens like SOL, USDC, etc (since those tokens are likely always top of the categories). * Default to 50 mints in response (use `limit` to increase or decrease number of results). ```js theme={null} const categoryResponse = await ( await fetch(`https://lite-api.jup.ag/tokens/v2/toporganicscore/5m?limit=100`) ).json(); ``` ## Get Recent The Tokens API V2 provides an endpoint to get mints and their mint information by their recency. This is helpful to display to users a list of tokens that just had their first pool created, providing more information to trade with. **RECENT** * Do note that the definition of RECENT is the **token's first pool creation time** (and not token's mint/creation timestamp). * Default to 30 mints in response. ```js theme={null} const recentResponse = await ( await fetch(`https://lite-api.jup.ag/tokens/v2/recent`) ).json(); ``` ## Example Response All endpoints will return an array of mints, along with their information. **Successful example response:** ```js expandable theme={null} [ { id: 'So11111111111111111111111111111111111111112', name: 'Wrapped SOL', symbol: 'SOL', icon: 'https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png', decimals: 9, circSupply: 531207433.3986673, totalSupply: 603724547.3627878, tokenProgram: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', firstPool: { id: '58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2', createdAt: '2021-03-29T10:05:48Z' }, holderCount: 2342610, audit: { mintAuthorityDisabled: true, freezeAuthorityDisabled: true, topHoldersPercentage: 1.2422471238911812 }, organicScore: 98.92390784896082, organicScoreLabel: 'high', isVerified: true, cexes: [ 'Binance', 'Bybit', 'OKX', 'Upbit', 'Bitget', 'Kraken', 'KuCoin', 'MEXC', 'Gate.io' ], tags: [ 'community', 'strict', 'verified' ], fdv: 87824499429.22047, mcap: 77275352037.79674, usdPrice: 145.47114211747515, priceBlockId: 349038717, liquidity: 89970631.83880953, stats5m: { priceChange: 0.021175445311831707, liquidityChange: -0.01230267453174984, volumeChange: 4.855149318222242, buyVolume: 14644327.188370818, sellVolume: 14743625.023908526, buyOrganicVolume: 269570.2345543641, sellOrganicVolume: 204114.37436445671, numBuys: 49281, numSells: 54483, numTraders: 18155, numOrganicBuyers: 981, numNetBuyers: 3503 }, stats1h: { priceChange: -0.145099593531635, liquidityChange: -0.13450589635262783, volumeChange: -15.928930753985316, buyVolume: 171520842.22567528, sellVolume: 174057197.5207193, buyOrganicVolume: 3099405.8562825476, sellOrganicVolume: 2975660.0383528043, numBuys: 586069, numSells: 649275, numTraders: 78145, numOrganicBuyers: 2716, numNetBuyers: 14442 }, stats6h: { priceChange: 0.3790495974473589, liquidityChange: 0.1659230330014905, volumeChange: 14.571340846647542, buyVolume: 1084625651.9256022, sellVolume: 1094488293.656417, buyOrganicVolume: 31145072.655369382, sellOrganicVolume: 31647431.25353508, numBuys: 3789847, numSells: 4363909, numTraders: 272131, numOrganicBuyers: 10849, numNetBuyers: 37155 }, stats24h: { priceChange: 1.5076363979360274, liquidityChange: 2.417364079880319, volumeChange: -2.1516094834673254, buyVolume: 4273248565.256824, sellVolume: 4306065610.69747, buyOrganicVolume: 109007133.8196669, sellOrganicVolume: 118085567.17983335, numBuys: 15125444, numSells: 17582713, numTraders: 754618, numOrganicBuyers: 28590, numNetBuyers: 80961 }, ctLikes: 4232, smartCtLikes: 522, updatedAt: '2025-06-25T05:02:21.034234634Z' } ] ``` # Best Practices Source: https://dev.jup.ag/docs/trigger/best-practices Some best practices when using the Trigger API. | Item | Recommendation | | :---------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The program will accept any value to create an order. | On jup.ag, our frontend enforces a minimum of 5 USD per order to be created, this will ensure our keepers can accommodate for no loss in transaction fees coverage. However, programmatically, if you do not enforce this, the user can still create an order. | | The program does not check the price or rate of the order, and the keeper will execute as instructed. | On our frontend, when user attempts to set the rate to buy above market price, we provide warnings and disable the execution if above 5%. If the order is created with such rates, the keeper will execute as instructed. For example, if user sets to Sell 1000 USDC to Buy 1 SOL at the rate of 1000 SOL/USDC, the keeper will execute as instructed and the additional funds will be lost. | | Tokens with transfer tax extension are disabled. | Our frontend informs the user if the token has transfer tax. | | Token2022 tokens with transfer tax extension are disabled. | Our frontend informs the user if the token has transfer tax. | | Trigger orders with slippage. | By nature, trigger orders execute with 0 slippage. However, you can add slippage to the order to ensure the order is filled but at the cost of slippage. | # Cancel Order Source: https://dev.jup.ag/docs/trigger/cancel-order **NOTE** * Lite URL: `https://lite-api.jup.ag/trigger/v1/cancelOrder` * Pro URL: `https://api.jup.ag/trigger/v1/cancelOrder` To upgrade to Pro or understand our rate limiting, please refer to this section. If you want to cancel order(s), you need to do these steps: 1. Get a list of the order accounts you want to cancel via `/getTriggerOrders` endpoint. 2. Use the list of order accounts to make a post request to the `/cancelOrder` endpoint to get the transaction to cancel one or multiple orders. 3. Sign then send the transaction to the network either via `/execute` endpoint or by yourself. **GET TRIGGER ORDERS** [Refer to the `/getTriggerOrders` section](/docs/trigger/get-trigger-orders) to prepare the list of order accounts you want to cancel. **INFO** To cancel multiple orders, you can use the [`/cancelOrders` endpoint](#cancel-orders) to pass in a list of order accounts and it will build the transaction for multiple cancellations. ## Cancel Order ```js theme={null} const cancelOrderResponse = await ( await fetch('https://lite-api.jup.ag/trigger/v1/cancelOrder', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ maker: "jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3", computeUnitPrice: "auto", order: "3g2jF8txqXPp6GUStwtXMrWydeYWxU4qoBA8UDLoTnK7", }) }) ).json(); ``` ## Cancel Order Response **Success Example Response** ```json theme={null} { "transaction": "AQAAAAAAAAAAAAAAAAAAAAAA......QYHAwUIX4Ht8Agx34Q=", "requestId": "370100dd-1a85-421b-9278-27f0961ae5f4", } ``` **Failed Example Response** ```json theme={null} { "error": "no matching orders found", "code": 400 } ``` ## Cancel Orders **WARNING** If no orders are specified, the API will return the transaction to cancel **ALL** open orders, batched in groups of 5 orders. **TIP** Orders are batched in groups of 5, if you have 6 orders to cancel, you will receive 2 transactions. Do note that you will receive a list of transactions, so you will need to access each transaction in it to sign and send individually. If using `/execute` endpoint, you should pass in the same `requestId` for the different transactions. ```js theme={null} const cancelOrdersResponse = await ( await fetch('https://lite-api.jup.ag/trigger/v1/cancelOrders', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ maker: "jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3", computeUnitPrice: "auto", orders: [ "6fe8ByaiFHisjnYnH5qdpyiNtkn89mMBQUemRkVmKhro", "9jwzPKHxcrSozdrTYzPnTqy7psRvNGxaYUAiiyxwZKjj" ] }) }) ).json(); ``` ## Cancel Orders Response **Success Example Response** ```json theme={null} { "transactions": [ "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA......DHfhA0JAAAJBQ0ODwsNCF+B7fAIMd+EDQkAAAMCDQ4PCw0IX4Ht8Agx34Q=", "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA......a8lAwQABQLAqAAABAAJAy48AAAAAAAABQkAAAIBBQYHAwUIX4Ht8Agx34Q=" ], "requestId": "370100dd-1a85-421b-9278-27f0961ae5f4", } ``` # Create Order Source: https://dev.jup.ag/docs/trigger/create-order **NEW PATHS** The `/limit/v2` path will be deprecated soon, please update your API calls to use the `/trigger/v1` path immediately. When updating to the new path, please refer to the documentation as there are some breaking changes. * `/execute` endpoint is introduced. * `/createOrder` endpoint now includes an additional `requestId` parameter to be used with the `/execute` endpoint. * `/cancelOrder` endpoint only builds the transaction for 1 order, while `/cancelOrders` endpoint builds the transaction for multiple orders. * The `tx` field in the responses are now `transaction` or `transactions`. * `/getTriggerOrders` endpoint is introduced to get either active or historical orders (based on the query parameters) in a new format. **NOTE** * Lite URL: `https://lite-api.jup.ag/trigger/v1/createOrder` * Pro URL: `https://api.jup.ag/trigger/v1/createOrder` To upgrade to Pro or understand our rate limiting, please refer to this section. ## Create Order This is a POST request to `/createOrder` endpoint, where you pass in the necessary parameters and our backend will create the transaction for you to sign and send to the network seamlessly. **OPTIONAL PARAMETERS** Do note that there are a few optional parameters that you can use, such as: * Adding slippage to the order: This corresponds to the "Ultra" mode on jup.ag frontend. Higher slippage increases execution success rate but may result in less favorable prices. Omitting this parameter (or setting it to 0) corresponds to "Exact" mode. [Learn more about UI modes vs API implementation](/docs/trigger#faq). * Setting an expiry date on the order. * Adding fees through our referral program, please ensure that your `feeAccount` has the necessary `referralTokenAccount`s of the output mint of the limit order for it to work, you can learn more about creating them dynamically in the [Add Fees To Swap](/docs/swap/add-fees-to-swap) guide. (Note that the fees are transferred only after the trigger order has been executed.) **Create a POST request to the `/createOrder` endpoint.** ```js theme={null} const createOrderResponse = await ( await fetch('https://lite-api.jup.ag/trigger/v1/createOrder', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ inputMint: inputMint.toString(), outputMint: outputMint.toString(), maker: "jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3", payer: "jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3", params: { makingAmount: "1000000", takingAmount: "300000", // slippageBps: "", // Optional, by nature, trigger orders execute with 0 slippage // expiredAt: "", // In unix seconds (e.g. Date.now()/1_000) or optional // feeBps: "", // Requires referral account or optional }, computeUnitPrice: "auto", // feeAccount: "", // Optional but if specified it is the referral token account of the output mint // wrapAndUnwrapSol: true, // Default true or optional }) }) ).json(); console.log(createOrderResponse); ``` Now that you have the order transaction, you can sign and send to the network. There are 2 methods, after signing the transaction, you can either send it to the network yourself or use the Trigger API's `/execute` endpoint to do it for you. ## Create Order Response **Success Example Response** ```json theme={null} { "order": "CFG9Bmppz7eZbna96UizACJPYT3UgVgps3KkMNNo6P4k", "transaction": "AQAAAAAAAAAAAAAAAAAAAAAA......AgAKCAkBAQsPAAADBAEMCwcKCQkIBg0LIoVuSq9wn/WfdskdmHlfUulAQg8AAAAAAICpAwAAAAAAAAAJAwEAAAEJAA==", "requestId": "370100dd-1a85-421b-9278-27f0961ae5f4" } ``` **Failed Example Response** ```json theme={null} { "error": "invalid create order request", "cause": "input mint making amount must be at least 5 USD, received: 2", "code": 400 } ``` # Execute Order Source: https://dev.jup.ag/docs/trigger/execute-order **NOTE** * Lite URL: `https://lite-api.jup.ag/trigger/v1/execute` * Pro URL: `https://api.jup.ag/trigger/v1/execute` To upgrade to Pro or understand our rate limiting, please refer to this section. After getting the order transaction, you can sign and send to the network yourself or use the Trigger API's `/execute` endpoint to do it for you. ## Sign Transaction Using the Solana `web3.js` v1 library, you can sign the transaction as follows: ```js theme={null} // ... GET /order's response // Extract the transaction from the order response const transactionBase64 = orderResponse.tx // Deserialize the transaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); // Sign the transaction transaction.sign([wallet]); // Serialize the transaction to base64 format const signedTransaction = Buffer.from(transaction.serialize()).toString('base64'); ``` ## Execute Order By making a post request to the `/execute` endpoint, Jupiter executes the order transaction on behalf of you/your users. This includes handling of transaction handling, priority fees, RPC connection, etc. ```js theme={null} const executeResponse = await ( await fetch('https://lite-api.jup.ag/trigger/v1/execute', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ signedTransaction: signedTransaction, requestId: "370100dd-1a85-421b-9278-27f0961ae5f4", }), }) ).json(); ``` ## Execute Order Response After making the post request to the `/execute` endpoint, you will receive a response with the status of the order. **Example response of successful order:** ```json theme={null} { "signature": "...", "status": "Success" } ``` **Example response of failed order:** ```json theme={null} { "error": "custom program error code: 1", "code": 500, "signature": "...", "status": "Failed" } ``` ## Send Transaction Yourself If you want to handle the transaction, you can sign and send the transaction to the network yourself. ```js expandable theme={null} const transactionBase64 = createOrderResponse.transaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); transaction.sign([wallet]); const transactionBinary = transaction.serialize(); const blockhashInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" }); const signature = await connection.sendRawTransaction(transactionBinary, { maxRetries: 1, skipPreflight: true }); const confirmation = await connection.confirmTransaction({ signature, blockhash: blockhashInfo.value.blockhash, lastValidBlockHeight: blockhashInfo.value.lastValidBlockHeight, }, "finalized"); if (confirmation.value.err) { throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}\n\nhttps://solscan.io/tx/${signature}`); } else console.log(`Transaction successful: https://solscan.io/tx/${signature}`); ``` # Get Trigger Orders Source: https://dev.jup.ag/docs/trigger/get-trigger-orders **NOTE** * Lite URL: `https://lite-api.jup.ag/trigger/v1/getTriggerOrders` * Pro URL: `https://api.jup.ag/trigger/v1/getTriggerOrders` To upgrade to Pro or understand our rate limiting, please refer to this section. This is a GET request to `/getTriggerOrders` endpoint. The response is paginated for every 10 orders and you can view different pages using the `page` parameter. The `hasMoreData` boolean will indicate if you have more data in the next page. **CHANGE OF RESPONSE FORMAT** The `/getTriggerOrders` endpoint does not provide the same data format as the old `orderHistory` or `openOrders` endpoint. ## Active Orders To get the active orders, you can pass in the `orderStatus` parameter as `active`. You can optionally pass in the input and output token mint addresses to filter the open orders. ```js theme={null} const openOrdersResponse = await ( await fetch( 'https://lite-api.jup.ag/trigger/v1/getTriggerOrders?user=jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3&orderStatus=active' ) ).json(); ``` ## Order History To get the order history, you can pass in the `orderStatus` parameter as `history`. ```js theme={null} const orderHistoryResponse = await ( await fetch( 'https://lite-api.jup.ag/trigger/v1/getTriggerOrders?user=ErJKdNoarixqGGQTHbBtvHtg2nkcCqcKtYjGbVKUxY7D&orderStatus=history' ) ).json(); ``` # About Trigger API Source: https://dev.jup.ag/docs/trigger/index The Jupiter Trigger API enables you to create limit orders on Solana, allowing users to set target prices for token swaps that execute automatically when market conditions are met. The Trigger API is ideal for: * DeFi applications that want to offer users more advanced trading options * Wallets looking to expand their trading features * Automated systems that need to execute at specific price points ## Features | Feature | Description | | :------------------------- | :---------------------------------------------------------------------------------------------------------- | | **Custom integrator fees** | Integrators can choose to charge their own custom fees (on top of Jupiter's fees). | | **Any token pair** | Create trigger orders between any token pairs supported on Jupiter's Metis Routing Engine. | | **Best execution** | Orders are executed through Jupiter's Metis Routing Engine to get the best possible price across all DEXes. | | **Price monitoring** | Our infrastructure continuously monitors prices to execute trigger orders as soon as conditions are met. | | **Order expiry** | Trigger orders can be set to expire after a certain period of time. | | **Slippage addition** | Add slippage to the target price, ideal for users who want to prioritize success rate over price. | ## Getting Started with Trigger API 1. [**Create Order**](/docs/trigger/create-order): Create a new trigger order with your desired parameters. 2. [**Execute Order**](/docs/trigger/execute-order): Execute a trigger order. 3. [**Cancel Order**](/docs/trigger/cancel-order): Cancel an existing trigger order. 4. [**Get Trigger Orders**](/docs/trigger/get-trigger-orders): Retrieve active/historical trigger orders for a specific wallet address 5. [**Best Practices**](/docs/trigger/best-practices): Best practices for using Trigger API. ## FAQ Trigger API takes 0.03% for stable pairs and 0.1% for every other pairs. Yes, integrators can take fees on top of Jupiter's fees. When using trigger orders on the jup.ag frontend, you'll see two execution modes: | jup.ag UI Mode | API Implementation | Description | | :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Exact** | `slippageBps: 0` (default) | Orders execute with 0 slippage for precise price execution

By default, trigger orders execute with 0 slippage (you can omit the `slippageBps` parameter) | | **Ultra** | `slippageBps: ` | Orders execute with custom slippage for higher success rates On the jup.ag UI, Ultra denotes that Jupiter sets the slippage for the user, however, in API implementation, you will need to evaluate and set it yourself. |
# Add Fees To Ultra Swap Source: https://dev.jup.ag/docs/ultra/add-fees-to-ultra Walkthrough to create the necessary accounts for adding fees to your Ultra Swap transaction. ## Key Points The Jupiter Ultra Swap API allows you to add integrator fees to the orders. The Ultra Swap Integrator Fees are governed by the [Referral Program](/tool-kits/referral-program). It is required to create a valid **referral account** and it's **referral token accounts** for the specific token mints to collect fees in. These accounts are initalized under the [**Jupiter Ultra Referral Project**](https://solscan.io/account/DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc). In the `/order` response, you will see the `feeMint` field which is the token mint we will collect the fees in for that particular order. Since Jupiter will always dictate which token mint to collect the fees in, you must ensure that you have the valid referral token account created for the specific fee mint. The `feeMint` is based on a priority list, you can refer to this [JSON file for the list](https://github.com/jup-ag/dynamic-slippage-config/blob/main/token_categories.json). For example, * If the `inputMint=SOL` and the `outputMint=USDC`, the `feeMint` will be `SOL`, as it is of highest priority. * If the `inputMint=USDC` and the `outputMint=SOL`, the `feeMint` will be `SOL`, as it is of highest priority, regardless of side. * If the `inputMint=MEME` and the `outputMint=USDC`, the `feeMint` will be `USDC`, as the stablecoin has higher priority. If the `referralTokenAccount` for the `feeMint` is not initialized, the order will still return and can be executed without your fees. This is to ensure your user still receives a quote to proceed with the swap. For example, if the `feeMint` is `SOL`, but the `referralTokenAccount` for `SOL` is not initialized, the order will still return and can be executed without your fees. You can refer to if `feeBps` tallies with what you specified in `referralFee`, in this case, the `feeBps` will default to Jupiter Ultra's default fees. By default, Jupiter Ultra Swap incurs a 5 to 10 bps fee based on token mint. When you add integrator fee, Jupiter will take a flat 20% of your integrator fees. For example, if you plan to take 100bps, Jupiter will take 20bps for the fee split (there will be no Ultra base fee). You can configure `referralFee` to be between 50bps to 255bps. The `/order` response will show the total fee in `feeBps` field which should be exactly what you specified in `referralFee`. Do note that, the referral token account has to be created before calling `/order` because during the request, we will check if the token account is initialized before applying your referral fee (if it is not applied, we will only apply our default fees). You can now take fees in SPL and Token2022 tokens. As long as `feeMint` is a Token2022 and you have the valid referral token account created for the specific fee mint, you can take fees in them. ## Limitations JupiterZ does not support integrator fees and it will not be routed when referral params are passed in to get an order. ## Step-by-step Install additional dependencies or if you prefer, you can use the [Referral Dashboard](https://referral.jup.ag/), a simple interface to create referral accounts. Create `referralAccount`. Create `referralTokenAccount` for each token mint. Add `referralAccount` and `referralFee` to Ultra Swap `/order` endpoint. Sign and send the transaction via Ultra Swap `/execute` endpoint. Verify transaction and fees. ```js theme={null} import { ReferralProvider } from "@jup-ag/referral-sdk"; import { Connection, Keypair, PublicKey, sendAndConfirmTransaction, sendAndConfirmRawTransaction } from "@solana/web3.js"; import fs from 'fs'; const connection = new Connection("https://api.mainnet-beta.solana.com"); const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const provider = new ReferralProvider(connection); const projectPubKey = new PublicKey('DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc'); async function initReferralAccount() { const transaction = await provider.initializeReferralAccountWithName({ payerPubKey: wallet.publicKey, partnerPubKey: wallet.publicKey, projectPubKey: projectPubKey, name: "insert-name-here", }); const referralAccount = await connection.getAccountInfo( transaction.referralAccountPubKey, ); if (!referralAccount) { const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); console.log('created referralAccountPubkey:', transaction.referralAccountPubKey.toBase58()); } else { console.log( `referralAccount ${transaction.referralAccountPubKey.toBase58()} already exists`, ); } } async function initReferralTokenAccount() { const mint = new PublicKey("So11111111111111111111111111111111111111112"); // the token mint you want to collect fees in const transaction = await provider.initializeReferralTokenAccountV2({ payerPubKey: wallet.publicKey, referralAccountPubKey: new PublicKey("insert-referral-account-pubkey-here"), // you get this from the initReferralAccount function mint, }); const referralTokenAccount = await connection.getAccountInfo( transaction.tokenAccount, ); if (!referralTokenAccount) { const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); console.log('created referralTokenAccountPubKey:', transaction.tokenAccount.toBase58()); console.log('mint:', mint.toBase58()); } else { console.log( `referralTokenAccount ${transaction.tokenAccount.toBase58()} for mint ${mint.toBase58()} already exists`, ); } } async function claimAllTokens() { const transactions = await provider.claimAllV2({ payerPubKey: wallet.publicKey, referralAccountPubKey: new PublicKey("insert-referral-account-pubkey-here"), }) // Send each claim transaction one by one. for (const transaction of transactions) { transaction.sign([wallet]); const signature = await sendAndConfirmRawTransaction(connection, transaction.serialize(), [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); } } // initReferralAccount(); // you should only run this once // initReferralTokenAccount(); // claimAllTokens(); ``` ### Dependencies ```bash theme={null} npm install @jup-ag/referral-sdk npm install @solana/web3.js@1 # Using v1 of web3.js instead of v2 ``` **Set up RPC Connection** Solana provides a [default RPC endpoint](https://solana.com/docs/core/clusters). However, as your application grows, we recommend you to always use your own or provision a 3rd party provider’s RPC endpoint such as [Helius](https://helius.dev/) or [Triton](https://triton.one/). ```bash theme={null} const connection = new Connection('https://api.mainnet-beta.solana.com'); ``` **Set up Development Wallet** You can paste in your private key for testing but this is not recommended for production. * Either use your private key in the project directly, you can do it via a `.env` file. * Or set up your private key in the [Solana CLI](https://solana.com/docs/intro/installation#create-wallet). ```javascript Store private key in .env theme={null} // In your .env file PRIVATE_KEY="" // In your index.js (or any file that needs the private key) import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || ''))); ``` ```javascript Store private key in Solana CLI theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` ### Create `referralAccount` * You should only need to create the referral account once. * After this step, you need to [create the referral token accounts for each token mint](#create-referraltokenaccount). ```js expandable theme={null} import { ReferralProvider } from "@jup-ag/referral-sdk"; import { Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js"; const connection = new Connection("https://api.mainnet-beta.solana.com"); const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const provider = new ReferralProvider(connection); const projectPubKey = new PublicKey('DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc'); // Jupiter Ultra Referral Project async function initReferralAccount() { const transaction = await provider.initializeReferralAccountWithName({ payerPubKey: wallet.publicKey, partnerPubKey: wallet.publicKey, projectPubKey: projectPubKey, name: "insert-name-here", }); const referralAccount = await connection.getAccountInfo( transaction.referralAccountPubKey, ); if (!referralAccount) { const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); console.log('created referralAccountPubkey:', transaction.referralAccountPubKey.toBase58()); } else { console.log( `referralAccount ${transaction.referralAccountPubKey.toBase58()} already exists`, ); } } ``` ### Create `referralTokenAccount` * You need to [create the `referralAccount` first](#create-referralaccount). * You need to create a `referralTokenAccount` for each token mint you want to collect fees in. * We don't recommend creating a token account for **every** token mint, as it costs rent and most tokens might not be valuable, instead created token accounts for top mints to begin with (you can always add more later). ```js expandable theme={null} import { ReferralProvider } from "@jup-ag/referral-sdk"; import { Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js"; const connection = new Connection("https://api.mainnet-beta.solana.com"); const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const provider = new ReferralProvider(connection); async function initReferralTokenAccount() { const mint = new PublicKey("So11111111111111111111111111111111111111112"); // the token mint you want to collect fees in const transaction = await provider.initializeReferralTokenAccountV2({ payerPubKey: wallet.publicKey, referralAccountPubKey: new PublicKey("insert-referral-account-pubkey-here"), mint, }); const referralTokenAccount = await connection.getAccountInfo( transaction.tokenAccount, ); if (!referralTokenAccount) { const signature = await sendAndConfirmTransaction(connection, transaction.tx, [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); console.log('created referralTokenAccountPubKey:', transaction.tokenAccount.toBase58()); console.log('mint:', mint.toBase58()); } else { console.log( `referralTokenAccount ${transaction.tokenAccount.toBase58()} for mint ${mint.toBase58()} already exists`, ); } } ``` ### Usage in Ultra Swap * After creating the necessary accounts, you can now add the `referralAccount` and `referralFee` to the Ultra Swap `/order` endpoint. * From the order response, you should see the `feeMint` field, which is the token mint we will collect the fees in for that particular order. * From the order response, you should see the `feeBps` field, which is the total fee in bps, which should be exactly what you specified in `referralFee`. * Then, you can sign and send the transaction via the Ultra Swap `/execute` endpoint. **DANGER** Do note that, during your request to `/order`, we will check if the specific fee mint's referral token account is initialized. If it is not, the order will still return and can be executed without your fees. This is to ensure success rates and the best experience with Jupiter Ultra Swap. Hence, please verify the transaction when testing with a new referral token account, and always create the referral token account before calling `/order`. ```js expandable theme={null} import { Keypair, VersionedTransaction } from "@solana/web3.js"; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const orderResponse = await ( await fetch( 'https://lite-api.jup.ag/ultra/v1/order?' + 'inputMint=So11111111111111111111111111111111111111112&' + 'outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&' + 'amount=100000000&' + 'taker=jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3&' + 'referralAccount=&' + // insert referral account public key here 'referralFee=50' // insert referral fee in basis points (bps) ) ).json(); console.log(JSON.stringify(orderResponse, null, 2)); const transactionBase64 = orderResponse.transaction // Extract the transaction from the order response const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); // Deserialize the transaction transaction.sign([wallet]); // Sign the transaction const signedTransaction = Buffer.from(transaction.serialize()).toString('base64'); // Serialize the transaction to base64 format const executeResponse = await ( await fetch('https://lite-api.jup.ag/ultra/v1/execute', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ signedTransaction: signedTransaction, requestId: orderResponse.requestId, }), }) ).json(); if (executeResponse.status === "Success") { console.log('Swap successful:', JSON.stringify(executeResponse, null, 2)); console.log(`https://solscan.io/tx/${executeResponse.signature}`); } else { console.error('Swap failed:', JSON.stringify(executeResponse, null, 2)); console.log(`https://solscan.io/tx/${executeResponse.signature}`); } ``` ## Claim All Fees * The `claimAllV2` method will return a list of transactions to claim all fees and are batched by 5 claims for each transaction. * The code signs and sends the transactions one by one - you can also Jito Bundle to send multiple at once, if preferred. * When claiming fees, the transaction will include the transfer of the fees to both your referral account and Jupiter's (20% of your integrator fees). ```js expandable theme={null} import { ReferralProvider } from "@jup-ag/referral-sdk"; import { Connection, Keypair, PublicKey, sendAndConfirmRawTransaction } from "@solana/web3.js"; const connection = new Connection("https://api.mainnet-beta.solana.com"); const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); const provider = new ReferralProvider(connection); async function claimAllTokens() { const transactions = await provider.claimAllV2({ payerPubKey: wallet.publicKey, referralAccountPubKey: new PublicKey("insert-referral-account-pubkey-here"), }) // Send each claim transaction one by one. for (const transaction of transactions) { transaction.sign([wallet]); const signature = await sendAndConfirmRawTransaction(connection, transaction.serialize(), [wallet]); console.log('signature:', `https://solscan.io/tx/${signature}`); } } ``` # Execute Order Source: https://dev.jup.ag/docs/ultra/execute-order Swap via Ultra by signing then submitting the transaction to the Execute endpoint . ## Sign Transaction Using the Solana `web3.js@1` library, you can sign the transaction as follows: **Set up dependencies for signing** ```bash theme={null} npm install @solana/web3.js@1 ``` **Set up Development Wallet** You can paste in your private key for testing but this is not recommended for production. * Either use your private key in the project directly, you can do it via a `.env` file. * Or set up your private key in the [Solana CLI](https://solana.com/docs/intro/installation#create-wallet). ```javascript Store private key in .env theme={null} // In your .env file PRIVATE_KEY="" // In your index.js (or any file that needs the private key) import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || ''))); ``` ```javascript Store private key in Solana CLI theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` ```js Sign Transaction theme={null} import { VersionedTransaction } from '@solana/web3.js'; // ... Get Order's response // Extract the transaction from the order response const transactionBase64 = orderResponse.transaction // Deserialize, sign and serialize the transaction const transaction = VersionedTransaction.deserialize(Buffer.from(transactionBase64, 'base64')); transaction.sign([wallet]); const signedTransaction = Buffer.from(transaction.serialize()).toString('base64'); ``` ## Execute Order By making a post request to the `/execute` endpoint, Jupiter executes the swap transaction on behalf of you/your users through our own proprietary transaction sending infrastructure. This already includes handling of slippage, priority fees, transaction landing and more. To make a post request to execute a swap order, you need to pass in the required parameters,: * `signedTransaction`: The signed and serialized base64 encodedtransaction [like above](#sign-transaction) * `requestId`: The order response's request ID [from Get Order](/docs/ultra/get-order) ```js Execute Order theme={null} const executeResponse = await ( await fetch('https://lite-api.jup.ag/ultra/v1/execute', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ signedTransaction: signedTransaction, requestId: orderResponse.requestId, }), }) ).json(); ``` ## Transaction Status Polling After our transaction sending service has submitted your swap, we will actively poll for your transaction status as part of the `/execute` endpoint. You will receive a response with the status of the swap. * You can submit with the same `signedTransaction` and `requestId` for **up to 2 minutes regardless of state**, to poll for the transaction status. * The transaction will not double execute since it has the same signature. * If connection got dropped, you can try again with the same `signedTransaction` and `requestId` to poll for the status of the swap. * If there is no status, the order likely expired (did not get processed onchain and failed), but reach out to us if cases like this happen. ```js Transaction Status Polling theme={null} if (executeResponse.status === "Success") { console.log('Swap successful:', JSON.stringify(executeResponse, null, 2)); console.log(`https://solscan.io/tx/${executeResponse.signature}`); } else { console.error('Swap failed:', JSON.stringify(executeResponse, null, 2)); console.log(`https://solscan.io/tx/${executeResponse.signature}`); } ``` # Get Holdings Source: https://dev.jup.ag/docs/ultra/get-holdings Ultra Swap API to get the detailed token holdings of an account. ```js theme={null} const holdingsResponse = await ( await fetch(`https://lite-api.jup.ag/ultra/v1/holdings/3X2LFoTQecbpqCR7G5tL1kczqBKurjKPHhKSZrJ4wgWc`) ).json(); console.log(JSON.stringify(holdingsResponse, null, 2)); ``` ## Holdings Response The holdings response will return the following: * A list of token holdings for the user's wallet address. * Token account information for each token holding. * Note that the top level response outside of `tokens` is the native SOL balance. :::tip For tokens with more than thousands of token holdings, the response may be slow - depending on the number of token holdings, the response time may vary. If you only need the native SOL balance, you can use `/holdings/{address}/native` to get the native SOL balance. ::: **Successful example response:** ```json theme={null} { "amount": "1000000000", "uiAmount": 1, "uiAmountString": "1", "tokens": { "jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v": [ { "account": "tokenaccountaddress", "amount": "1000000000", "uiAmount": 1, "uiAmountString": "1", "isFrozen": false, "isAssociatedTokenAccount": true, "decimals": 9, "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" } ] } } ``` **Failed example response:** ```json theme={null} { "error": "Invalid address" } ``` # Get Order Source: https://dev.jup.ag/docs/ultra/get-order Swap via Ultra by getting a quote from the Order endpoint. To get an Ultra Swap order, you need to pass in the required parameters such as: | Parameter | Description | | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | | `inputMint` | The input token mint address | | `outputMint` | The output token mint address | | `amount` | The amount of input token to swap, in native token units (before decimals) | | `taker` | The user's wallet address (**Note:** If the `taker` is not provided, there will still be an Order Response with no `transaction` field) | | `referralAccount` | The referral account address - refer to the [Add Fees To Ultra Swap](/docs/ultra/add-fees-to-ultra) guide for the step by step process | | `referralFee` | The referral fee in basis points (bps) | ```js Get Order theme={null} const orderResponse = await ( await fetch( 'https://lite-api.jup.ag/ultra/v1/order' '?inputMint=So11111111111111111111111111111111111111112' + '&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' + '&amount=100000000' + '&taker=jdocuPgEAjMfihABsPgKEvYtsmMzjUHeq9LX4Hvs7f3' ) ).json(); ``` ## Order Response In the order response, you will receive a number of fields that are important to note of, such as the `swapType`, `slippageBps`, etc. The main fields you should need: * `transaction`: The base64 encoded transaction that you need to sign before submitting to the network. * `requestId`: The request ID of the order to be used in the `Execute Order` endpoint. Now, you are able to get a swap order, next steps is to make a post request to the `Execute Order` endpoint. [Let's go](/docs/ultra/execute-order)! # Get Shield Source: https://dev.jup.ag/docs/ultra/get-shield Ultra Swap API to retrieve token information and associated warnings for the specified mint addresses. This is useful when integrating with Jupiter Ultra Swap or any other APIs, allowing you or your user to be informed of any potential malicious mints before conducting your transaction. ```js theme={null} const shieldResponse = await ( await fetch(`https://lite-api.jup.ag/ultra/v1/shield?mints=So11111111111111111111111111111111111111112,EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v,someTokenAddressForEducationalPurposes`) ).json(); ``` ## Shield Response The shield response will return a list of objects, containing the token information and warnings of the mints passed in. Do note that this is subject to changes, and we will be adding more warnings and improving the accuracy of the warnings over time. For the full list of potential warnings, refer to the [Shield API Reference](/api-reference/ultra/shield). **Successful example response:** ```json expandable theme={null} { "warnings": { "someTokenAddressForEducationalPurposes": [ { "type": "NOT_VERIFIED", "message": "This token is not verified, make sure the mint address is correct before trading", "severity": "info" }, { "type": "LOW_ORGANIC_ACTIVITY", "message": "This token has low organic activity", "severity": "info" }, { "type": "NEW_LISTING", "message": "This token is newly listed", "severity": "info" } ], "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": [ { "type": "HAS_FREEZE_AUTHORITY", "message": "The authority's owner has the ability to freeze your token account, preventing you from further trading", "severity": "warning" }, { "type": "HAS_MINT_AUTHORITY", "message": "The authority's owner has the ability to mint more tokens", "severity": "info" } ], "So11111111111111111111111111111111111111112": [] } } ``` # Get Started Source: https://dev.jup.ag/docs/ultra/get-started Get started with the Ultra Swap API. ## Overview | Step | Endpoint | Description | | :--- | :---------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | | 1 | [**Get Order**](/docs/ultra/get-order) | Request for a quote and swap transaction. | | 2 | [**Execute Order**](/docs/ultra/execute-order) | Sign and execute the swap transaction. | | - | [**Get Holdings**](/docs/ultra/get-holdings) | Request for token balances of an account. | | - | [**Get Shield**](/docs/ultra/get-shield) | Enhanced security feature to provide critical token information contributing to better informed trading decisions. | | - | [**Search Token**](/docs/ultra/search-token) | Search for a token by its symbol, name or mint address. | | - | [**Add Fees to Ultra Swap**](/docs/ultra/add-fees-to-ultra) | Add custom integrator fees to your Ultra Swap transaction, on top of Jupiter's fees. | ## FAQ * **Integrator without custom fees**: Do note that when your users swap using Ultra Swap, we take 5 to 10 bps of the swap amount as a fee. * **Integrator with custom fees**: If you are an integrator, you can add custom integrator fees via Ultra Swap API and Jupiter will take 20% of the integrator fees. Please refer to the [Add Fees To Ultra Swap](/docs/ultra/add-fees-to-ultra) guide for more information. * No, you cannot modify Ultra Swap transactions. * Ultra Swap is intended to use as is, without any modifications. * Dynamic Rate Limits are now applied to Ultra Swap API. * No Pro plans or payment needed. * Simply generate the universal API Key via [Portal](https://portal.jup.ag) * Rate limits scale together with your swap volume. * [Read more about Ultra Swap API Dynamic Rate Limit](/portal/rate-limit). # Overview Source: https://dev.jup.ag/docs/ultra/index Overview of Ultra Swap and its features. Ultra Swap is the most advanced yet developer-friendly solution for building trading applications on Solana. Ultra Swap is designed to be the only solution you’ll ever need for creating exceptional trading experiences. ## Quick Launch RPC-less architecture and Jupiter handles all trading optimizations for you. Easiest way to integrate full end-to-end Ultra Swap interface. ## Features Ultra Swap utilizes the latest [Juno Liquidity Engine](/docs/routing) which aggregates across multiple liquidity sources, including Jupiter's proprietary routing engines both Metis and Jupiter Z (RFQ), and third-party liquidity sources, for the best possible price. It also includes self-learning capabilities (to detect and sideline low-quality liquidity sources) which creates a competitive environment for all liquidity sources to continously optimize their performance and price. Over the years of experiment and development, we have optimized and continues to better our transaction sending service. Using various strategies involving multiple RPCs, priority fees/Jito tips estimation, and more, we are able to send transactions at a blazing fast speed with a high success rate (while still being [MEV-protected](#mev-protection)). 95% of all swaps are executed under 2 seconds via our proprietary transaction sending engine. For more information on latencies, [refer to the latency section](#latency). According to both our internal monitoring system and external resources such as [Sandwiched.me](https://sandwiched.me/sandwiches), Ultra has the lowest incidence of MEV attacks across all existing applications, by far. Comparing using the ratio of volume to the amount of value extracted, Ultra Swap has the highest volume yet the lowest value extracted. Building on top of our previous versions of slippage estimation/optimization engines, we have developed a new Real Time Slippage Estimator (RTSE), that is able to intelligently estimate the best possible slippage to use at the time of execution, balancing between trade success and price protection. RTSE uses a variety of heuristics, algorithms and monitoring to ensure the best user experience: * **Heuristics**: Token categories, historical and real-time slippage data, and more. * **Algorithms**: Exponential Moving Average (EMA) on slippage data, and more. * **Monitoring**: Real-time monitoring of failure rates to ensure reactiveness to increase slippage when necessary. Ultra Swap provides different gasless mechanisms for different scenarios. * **Gasless via Jupiter Z (RFQ)**: All swaps routed via Jupiter Z are gasless, as the market maker is the fee payer for the transaction. * **Gasless via Gasless Support**: Depending on the tokens and trade sizes of your swap, Ultra Swap will automatically determine if it can provide gasless support to your swap by helping you pay for the transaction fee of your swap - you can identify this via the secondary signer in the transaction. 95% of all swaps are executed under 2 seconds via our proprietary transaction sending engine. | Endpoint | Description | Latency (P90 Average) | | :---------- | :---------------------------------------------------------------------------------------------------- | :------------------------ | | `/order` | Aggregating across multiple liquidity sources and selecting the best price. | 500ms | | `/execute` | Broadcasting the transaction to the network and polling for the status and result of the transaction. | Metis: 1.5s; JupiterZ: 5s | | `/balances` | Retrieving the user's balances. | 200ms | | `/shield` | Enhanced token security feature to provide critical token information. | 400ms | | Feature | Description | | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Best Trading Experience** | Ultra Swap is the best trading experience in crypto, it handles all the complexities and headaches such as slippage protection, transaction landing and more. | | **RPC-less** | You do not need to provide a RPC endpoint to send transactions, get token information, or get user balances - we handle everything for you. | | **Holistic Coverage** | Ultra Swap covers all the necessary features for you to build your application, including the features mentioned below and useful information such as user wallet balances, token information, and more. | | **Integrator Fees** | Ultra Swap allows you to add custom integrator fees to your transactions, on top of Jupiter's fees. Refer to the [Add Fees To Ultra Swap](/docs/ultra/add-fees-to-ultra) guide for more information. | | **Developer Support** | Get the [best developer support in our Discord](https://discord.gg/jup). | | **World Class Support** | Ultra Swap is the best trading experience in crypto, it handles all the complexities such as slippage protection and transaction landing, and if you ever face any issues or need help when using Ultra Swap, our support team is here to assist you 24/7. Read more about [Ultra Swap Customer Support](/resources/support#customer-support). | ## What About Legacy Swap API? Ultra Swap API is the next evolution of our swap infrastructure, designed and continuously optimized to deliver the best possible trading experience on Solana. We are focused on providing a high-performance, reliable, and feature-rich API that incorporates the latest advancements and optimizations for users and integrators. While Ultra Swap API is engineered for optimal performance and ease of integration, the Legacy Swap API remains available for developers who require advanced customization, such as: * Adding custom instructions. * Incorporating Cross Program Invocation (CPI) calls. * Selecting specific broadcasting strategies for signed transactions (e.g., priority fee, Jito, etc.). * Choosing which DEXes or AMMs to route through. * Modifying the number of accounts used in a transaction. If your use case demands these highly custom features, the Legacy Swap API may be more suitable. However, it also requires you to manage many aspects that Ultra Swap API handles automatically, including: * **RPC management**: Retrieving wallet balances, broadcasting, and tracking transactions. * **Transaction fee selection**: Managing priority fees, Jito fees, and more. * **Slippage optimization**: Determining the best slippage settings for trade success and price protection. * **Transaction broadcasting**: Ultra Swap leverages a proprietary transaction engine for superior speed and reliability. * **Swap result parsing**: Handling transaction polling, parsing, and error management. For most developers and applications, Ultra Swap API is the recommended choice, as it is purpose-built and continually improved to provide the best possible swap experience on Solana. # Integrate Jupiter Plugin Source: https://dev.jup.ag/docs/ultra/plugin-integration Seamlessly integrate end-to-end Ultra Swap functionality into any application with just a few lines of code. Jupiter Plugin is an open-source, lightweight, plug-and-play version of Jupiter Ultra Swap, allowing you to bring the exact jup.ag swap experience to any application. Try out the [Plugin Playground](https://plugin.jup.ag/) to experience the entire suite of customizations. To view the open-source code, visit the [GitHub repository](https://github.com/jup-ag/plugin). Plugin Playground { e.target.style.opacity = '1'; e.target.style.border = '2px solid #C8F284'; }} onMouseLeave={(e) => { e.target.style.opacity = '1'; e.target.style.border = '2px solid transparent'; }} data-og-width="3546" width="3546" data-og-height="2052" height="2052" data-path="static/images/plugin-playground.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=280&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=8395349bbf7f758ca18591330ca87fc7 280w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=560&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=46e08b413d1673216c5a1c4d57126c91 560w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=840&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=2914dd9f251c0f3df220e1a31e04f63c 840w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=1100&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=9d4185743a5a3991ac7e595443788fc8 1100w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=1650&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=a7ebc84c3d7e2d259f20c50d8fb2cb13 1650w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=2500&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=ce6b355f81e4a7e0d9e69945e8c0a5a7 2500w" /> **QUICK START** To quick start your integration, check out the [Next.js](/tool-kits/plugin/nextjs-app-example), [React](/tool-kits/plugin/react-app-example) or [HTML](/tool-kits/plugin/html-app-example) app examples. Refer to [Customization](/tool-kits/plugin/customization) and [FAQ](/tool-kits/plugin/faq) for more information. ## Key Features * **Seamless Integration**: Embed Jupiter's swap functionality directly into your application without redirects. * **Multiple Display Options**: Choose between integrated, widget, or modal display modes. * **Customizable Options**: Configure the swap form to match your application's needs. * **RPC-less**: Integrate Plugin without any RPCs, Ultra handles transaction sending, wallet balances and token information. * **Ultra Mode**: Access to all features of Ultra Mode, read more about it in the [Ultra Swap API docs](/docs/ultra/). ## Getting Started When integrating Plugin, there are a few integration methods to think about, and choose the one that best fits your application's architecture and requirements. ### Integration Methods * **Using Window Object** - Simplest way to add and initialize Plugin. * [**Using NPM Package**](https://www.npmjs.com/package/@jup-ag/plugin) - Install via `npm install @jup-ag/plugin` and initialize as a module (will require you to maintain its dependencies). ### Wallet Integration * **Wallet Standard Support**: For applications without existing wallet provider, Plugin will provide a wallet adapter and connection - powered by [Unified Wallet Kit](/tool-kits/wallet-kit/). * **Passthrough Wallet**: For applications with existing wallet provider(s), set `enableWalletPassthrough=true` with context, and Plugin will allow the application to pass through the existing wallet provider's connection to Plugin. ### Adding Fees to plugin * **Referral Account**: You can create a referral account via [scripts](/docs/ultra/add-fees-to-ultra) or [Referral Dashboard](https://referral.jup.ag/). * **Referral Fee**: You can set the referral fee and account in the `formProps` interface when you initialize the Plugin. ### Quick Start Guides In the next sections, we'll walk you through the steps to integrate Jupiter Plugin into different types of web applications from scratch. By integrating Jupiter Plugin into your application, you can seamlessly integrate a fully functional swap interface into your application with minimal effort, while staying at the forefront of Solana DeFi innovation. # Rate Limit Source: https://dev.jup.ag/docs/ultra/rate-limit The Ultra Swap API uses a unique rate limiting mechanism that scales with your executed swap volume over time. ## Overview | Property | Dynamic | | :---------------------- | :------------------------------------------- | | **Base URL** | `https://api.jup.ag/ultra/` | | **Cost** | Free to use, but Ultra Swap incurs swap fees | | **API Key** | Required | | **Requests Per Minute** | Base Quota + Added Quota | ## API Key Rules The Ultra Swap API requires an API key to be used and can be generated via [Portal](https://portal.jup.ag). * It is required to be used for the Dynamic Rate Limit. * The API Key is free to generate and is universal, the API Key will work for all APIs. * Upgrading to a Pro plan only applies to other APIs, does not work with Ultra Swap API. ## How Dynamic Rate Limit Works Every **10 minutes** * The system aggregates your swap volume from `/execute` on Ultra Swap for **the current rolling day** (volume of (current timestamp - 1 day) up to present). * After which, the Added Quota will update, which will be added on top of the Base Quota. | Swap Volume | Requests Per Period | Sliding Window Period | | :---------- | :------------------------ | :-------------------- | | \$0 | 50 Base + 0 Added = 50 | 10 seconds | | \$10,000 | 50 Base + 1 Added = 51 | 10 seconds | | \$100,000 | 50 Base + 11 Added = 61 | 10 seconds | | \$1,000,000 | 50 Base + 115 Added = 165 | 10 seconds | The formula is subject to changes as we experiment with the Dynamic Rate Limit system. If you find that the rate limit is too restrictive, please reach out to us in [Discord](https://discord.gg/jup). ## Managing Rate Limits If you receive a 429 response, you should: 1. Implement exponential backoff in your retry logic 2. Wait for sliding window to allow for more requests 3. **Scale your Ultra Swap usage** to unlock higher limits or reach out to us in [Discord](https://discord.gg/jup). Bursting beyond your allocation may result in **temporary 429s/rate limits**, even after the refill period. Avoid aggressive retry patterns. # Response Source: https://dev.jup.ag/docs/ultra/response Ultra Swap API Responses that can be returned from both the `/order` and `/execute` endpoints. ## Order Response ```json Metis theme={null} { "mode": "ultra", "inAmount": "100000000", "outAmount": "461208958", "otherAmountThreshold": "460024271", "swapMode": "ExactIn", "slippageBps": 26, "priceImpactPct": "-0.0001311599520149334", "routePlan": [ { "swapInfo": { "ammKey": "HTvjzsfX3yU6BUodCjZ5vZkUrAxMDTrBs3CJaq43ashR", "label": "MeteoraDLMM", "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outputMint": "So11111111111111111111111111111111111111112", "inAmount": "52000000", "outAmount": "239879552", "feeAmount": "0", "feeMint": "11111111111111111111111111111111" }, "percent": 52, "bps": 5200 } ], "feeMint": "So11111111111111111111111111111111111111112", "feeBps": 2, "taker": "taker-address", "gasless": false, "signatureFeeLamports": 5000, "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAEB+r/6dWfRh5QZq1pS2FU/a5CQmMX/HcgLK4+zaeSlI5cZdTk+KNaH68Jj2ISScdmgdJ/88PKxKtXPavfMK2A5TFvQBO6cleTQsKnQWYpDA5PurAceVrkoCVPKJSGBw6LARo6wTdxxXRAzu6pCAqBH8SnExvClVC1O8bT5gyQxm5oAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpeE6XgwQiPQTdYGYpJIn7N9ynBymyDkUCCC1aA3Klx+cFBAAFAjh+AQAEAAkD75JsAAAAAAAFBQEAFAYQCZPxe2T0hK52/wUhAAIBEhQGBgURBQcWCggLCQwCAQAGFRcADQECDw4DFQYTLrtk+swxxK8UAOH1BQAAAAAbtn0bAAAAABoAAgAAAAIAAABDUBQAAlYB/8ASAAIGAwEAAAEJAym/lQcqT78E33F1k+c4vMwhJygVwkcagNn59VWw1IQlASAFEwAoAhdSojJcrqzlDmuUV2ZVdk3ihN14mZpQMxrnUJS463C7xAUxLhQVMAItL7MrMZDyYzurr+hCn5YjvrvmsR9/EdfdhYUISn5ODzRsA8vPzAHO", "prioritizationFeeLamports": 696237, "rentFeeLamports": 0, "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outputMint": "So11111111111111111111111111111111111111112", "swapType": "aggregator", "router": "metis", "requestId": "019974a8-5fbb-7395-9355-9ebf8f844884", "inUsdValue": 99.96761068334662, "outUsdValue": 99.95449893632635, "priceImpact": -0.013115995201493341, "swapUsdValue": 99.96761068334662, "totalTime": 359 } ``` ```json JupiterZ (RFQ) theme={null} { "mode": "ultra", "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outputMint": "So11111111111111111111111111111111111111112", "inAmount": "100000000", "outAmount": "460250418", "otherAmountThreshold": "460250418", "swapMode": "ExactIn", "slippageBps": 0, "priceImpactPct": "-0.00018881197024002837", "routePlan": [ { "swapInfo": { "ammKey": "CDg3bPoM21fSXEzrXWHWyJR33JHX6xaYboq5p7s4uo48", "label": "JupiterZ", "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outputMint": "So11111111111111111111111111111111111111112", "inAmount": "100000000", "outAmount": "460250418", "feeAmount": "0", "feeMint": "11111111111111111111111111111111" }, "percent": 100, "bps": 10000 } ], "feeBps": 2, "transaction": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAIABgymr6yEAmkOIXpp3AcyvWL/QB2KoDZDEZAXeMYvfP7cver/6dWfRh5QZq1pS2FU/a5CQmMX/HcgLK4+zaeSlI5cDiUHiMuOTEfv/7RvoyR8p3lT/6Mq87vOJ1wZbQZg8PAPZvgVNXGQR4l/GibNBPjRJtllQ2kqGDcrpjCUaFKkdTAkgVB1V0ZN+ftspt1cSBk+wVMQVrs4JNQFQwBgW9GZb0ATunJXk0LCp0FmKQwOT7qwHHla5KAlTyiUhgcOiwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMGRm/lIRcy/+ytunLDm+e8jOW7xfcSayxDmzpAAAAABpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAEG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqUpYSftyo7vpH9xbDmpX9jxaHLRbIGem7Qys02OVyKECxvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWGJKbAQWdHOsOJGn5YdWJhd2PXDdDG9WzYvCihrfITlwwMHAAkDDRoAAAAAAAAHAAUC4JAAAAoMAQAFAwoCCwkICQYEI6hgt6NcCiigAOH1BQAAAAAy3W4bAAAAACoW0mgAAAAAAgAAAA==", "gasless": true, "signatureFeeLamports": 0, "prioritizationFeeLamports": 0, "rentFeeLamports": 0, "requestId": "ff63982b-9140-9b0e-e525-44f7246a79b2", "swapType": "rfq", "router": "jupiterz", "quoteId": "5852f88e-525b-5400-ab97-abe5e409ebfd", "maker": "CDg3bPoM21fSXEzrXWHWyJR33JHX6xaYboq5p7s4uo48", "taker": "taker-address", "expireAt": "1758598698", "platformFee": { "amount": "92050", "feeBps": 2 }, "inUsdValue": 99.97072758461792, "outUsdValue": 99.95185191457634, "priceImpact": -0.018881197024002837, "swapUsdValue": 99.97072758461792, "totalTime": 489 } ``` In cases where it fails to find a quote, the response will be as follows. ```json 400 Response Code theme={null} { "error": "Failed to get quotes" } ``` In cases where a quote is available but the swap simulation fails, there are error codes that can be returned. | `errorCode` | `errorMessage` | Description | | :---------- | :---------------------------------- | :----------------------------------------------------------------------- | | 1 | Insufficient funds | Does not have sufficient swap amount | | 2 | Top up `${solAmount}` SOL for gas | Does not have sufficient SOL for gas fees | | 3 | Minimum `${swapAmount}` for gasless | Does not have sufficient trade size to be applicable for Gasless Support | ## Execute Response Use the `signature` field to view the transaction in an explorer. ```json theme={null} { "status": "Success", "signature": "transaction-signature", "slot": "323598314", "code": 0, "inputAmountResult": "9995000", "outputAmountResult": "1274698", "swapEvents": [ { "inputMint": "So11111111111111111111111111111111111111112", "inputAmount": "9995000", "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "outputAmount": "1274698" } ] } ``` Here are some example cases of the execute response when it fails. ```json Ultra Endpoint Codes theme={null} { "code": -1, "error": "Order not found, it might have expired" } ``` ```json Aggregator Swap Type Codes theme={null} { "status": "Failed", "slot": "0", "signature": "transaction-signature", "code": -1005, "error": "Transaction expired" } ``` ```json RFQ Swap Type Codes theme={null} { "status": "Failed", "slot": "0", "code": -2005, "error": "Internal error", } ``` In cases where the routes are of aggregator types like Metis and has been submitted to the network, but failed to land due to program related errors, the response will be as follows. Only Jupiter V6 Aggregator Program Codes are parsed with description, for other DEX program codes, the error message can be `custom program error: #`. ```json Program Related Codes theme={null} { "status": "Failed", "signature": "transaction-signature", "slot": "368661931", "code": 6001, "error": "Slippage tolerance exceeded", "totalInputAmount": "1000000", "totalOutputAmount": "4647512", "inputAmountResult": "1000000", "outputAmountResult": "4648441", "swapEvents": [ { "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "inputAmount": "50000", "outputMint": "So11111111111111111111111111111111111111112", "outputAmount": "232423" }, { "inputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "inputAmount": "950000", "outputMint": "So11111111111111111111111111111111111111112", "outputAmount": "4416018" } ] } ``` ### Ultra Endpoint Codes | Code | Description | Debugging | | :--- | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | | 0 | Success | - | | -1 | Missing cached order | `requestId` not found in cache, likely expired or not found | | -2 | Invalid signed transaction | `signedTransaction` is invalid, likely failed to sign the transaction correctly | | -3 | Invalid message bytes | `signedTransaction` is invalid, likely due to incorrect usage like modification of `transaction` field in the order response | | -4 | Missing request id | `requestId` is not found in the request to `/execute` | | -5 | Missing signed transaction | `signedTransaction` is not found in the request to `/execute` | ### Aggregator Swap Type Codes | Code | Description | Debugging | | :---- | :--------------------------- | :------------------------------------------------------------- | | -1000 | Failed to land | Transaction failed to land on the network | | -1001 | Unknown error | Please try again, if it persists please reach out in Discord | | -1002 | Invalid transaction | Please try again, if it persists please reach out in Discord | | -1003 | Transaction not fully signed | Failed to sign the transaction correctly | | -1004 | Invalid block height | The block height is invalid | | -1005 | Expired | The submitted transaction has been attempted but has expired | | -1006 | Timed out | The submitted transaction has been attempted but has timed out | | -1007 | Gasless unsupported wallet | The wallet is not supported for gasless | ### RFQ Swap Type Codes | Code | Description | Debugging | | :---- | :-------------- | :------------------------------------------------------------------- | | -2000 | Failed to land | Please try again, if it persists please reach out in Discord | | -2001 | Unknown error | Please try again, if it persists please reach out in Discord | | -2002 | Invalid payload | Please try again, if it persists please reach out in Discord | | -2003 | Quote expired | User did not respond in time or RFQ provider did not execute in time | | -2004 | Swap rejected | User or RFQ provider rejected the swap | | -2005 | Internal error | Please try again, if it persists please reach out in Discord | ### Program Related Codes For Jupiter V6 Aggregator Program Codes, the error message will be parsed with description. For the full and most up to date list of Jupiter V6 Aggregator Program Codes, you can refer to the IDL on an explorer. If you need help identifying the other DEX program codes, please reach out in Discord. ## Best Practices It is important to understand the error codes to provide a better experience for your users, helping them make an informed decision or follow up step to help their transaction succeed. See [https://jup.ag/](https://jup.ag/) as a reference to understand how we handle errors on the UI. | Example Error | Best Practice | | :--------------------------- | :----------------------------------------------------------------------------------------------------------- | | Slippage exceeding threshold | Show the user the current slippage tolerance and the incurred slippage | | Insufficient funds | Disable swap widget but still provide quote visibility | | Non Jupiter Program Errors | Allow the user to retry with a new route by requoting and/or exclude the specific DEX from the quote request | # Search Token Source: https://dev.jup.ag/docs/ultra/search-token The Ultra Swap API provides an endpoint to search tokens in the background for you and returns you the search results, along with the mint information. This is useful in most user applications, as users need to choose which tokens they want to swap. This also provides a seamless developer experience as integrating this allows us to handle and abstract the token search mechanism, allowing you to focus on other user features. **SEARCH** * Search for a token and its information by its **symbol, name or mint address**. * Comma-separate to search for multiple. * Limit to 100 mint addresses in query. * Default to 20 mints in response when searching via symbol or name. ```js theme={null} const searchResponse = await ( await fetch(`https://lite-api.jup.ag/ultra/v1/search?query=So11111111111111111111111111111111111111112`) ).json(); ``` ## Search Response The search response will return an array of mints, along with their information. **USEFUL MINT INFORMATION** * Token Metadata like name, symbol, icon to display token information to users * [Organic Score](/docs/tokens/organic-score), Holder count, Market cap, etc can be useful to help make a better trading decision * And much more! Do note that the response is subject to changes as we continue to improve. Refer to [Ultra Swap API Reference](/api-reference/ultra/search) for full schema. **Successful example response:** ```js expandable theme={null} [ { id: 'So11111111111111111111111111111111111111112', name: 'Wrapped SOL', symbol: 'SOL', icon: 'https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png', decimals: 9, circSupply: 531207433.3986673, totalSupply: 603724547.3627878, tokenProgram: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', firstPool: { id: '58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2', createdAt: '2021-03-29T10:05:48Z' }, holderCount: 2342610, audit: { mintAuthorityDisabled: true, freezeAuthorityDisabled: true, topHoldersPercentage: 1.2422471238911812 }, organicScore: 98.92390784896082, organicScoreLabel: 'high', isVerified: true, cexes: [ 'Binance', 'Bybit', 'OKX', 'Upbit', 'Bitget', 'Kraken', 'KuCoin', 'MEXC', 'Gate.io' ], tags: [ 'community', 'strict', 'verified' ], fdv: 87824499429.22047, mcap: 77275352037.79674, usdPrice: 145.47114211747515, priceBlockId: 349038717, liquidity: 89970631.83880953, stats5m: { priceChange: 0.021175445311831707, liquidityChange: -0.01230267453174984, volumeChange: 4.855149318222242, buyVolume: 14644327.188370818, sellVolume: 14743625.023908526, buyOrganicVolume: 269570.2345543641, sellOrganicVolume: 204114.37436445671, numBuys: 49281, numSells: 54483, numTraders: 18155, numOrganicBuyers: 981, numNetBuyers: 3503 }, stats1h: { priceChange: -0.145099593531635, liquidityChange: -0.13450589635262783, volumeChange: -15.928930753985316, buyVolume: 171520842.22567528, sellVolume: 174057197.5207193, buyOrganicVolume: 3099405.8562825476, sellOrganicVolume: 2975660.0383528043, numBuys: 586069, numSells: 649275, numTraders: 78145, numOrganicBuyers: 2716, numNetBuyers: 14442 }, stats6h: { priceChange: 0.3790495974473589, liquidityChange: 0.1659230330014905, volumeChange: 14.571340846647542, buyVolume: 1084625651.9256022, sellVolume: 1094488293.656417, buyOrganicVolume: 31145072.655369382, sellOrganicVolume: 31647431.25353508, numBuys: 3789847, numSells: 4363909, numTraders: 272131, numOrganicBuyers: 10849, numNetBuyers: 37155 }, stats24h: { priceChange: 1.5076363979360274, liquidityChange: 2.417364079880319, volumeChange: -2.1516094834673254, buyVolume: 4273248565.256824, sellVolume: 4306065610.69747, buyOrganicVolume: 109007133.8196669, sellOrganicVolume: 118085567.17983335, numBuys: 15125444, numSells: 17582713, numTraders: 754618, numOrganicBuyers: 28590, numNetBuyers: 80961 }, ctLikes: 4232, smartCtLikes: 522, updatedAt: '2025-06-25T05:02:21.034234634Z' } ] ``` # Development Basics Source: https://dev.jup.ag/get-started/development-basics Learn the basics of Solana and Jupiter development, including interacting with Solana programs and accounts, building transactions, and sending transactions to the network. Solana uses an account-based architecture where data are stored in accounts. However, Solana keeps Programs (also known as smart contracts on other blockchains) and Accounts distinct. **Where are the Jupiter Programs deployed?** Jupiter is built on Solana MAINNET only! In order to mutate the data in Accounts, you will need to send transactions to the network which execute Instructions defined by Programs. * [Programs](https://solana.com/docs/core/programs) on Solana are executable code deployed on-chain. They are designed to execute instructions, process transactions and interact with accounts. * [Instructions](https://solana.com/docs/core/transactions#instruction) on Solana are defined by the Program, similar to API endpoints exposed by a program. * [Accounts](https://solana.com/docs/core/accounts) store data and are mutable, meaning they can be updated by the program who interacts with them. * [Transactions](https://solana.com/docs/core/transactions#transaction) is what we send to interact with the network which can include one or more instructions to execute what is needed. ## Interacting with Solana The Solana Web3.js and Rust client libraries serve as essential interfaces for interacting with Solana in JavaScript/TypeScript and Rust environments, respectively. They abstract complex interactions with the network, providing easier and more accessible functions for developers building on Solana. Here’s an overview of what each library offers and some of the most common functions they simplify: 1. Connecting to the network via RPC (Remote Procedure Call) endpoints 2. Building Transactions 3. Interfacing with Solana Programs and Accounts Explore the rich features and detailed documentation of these libraries in the official Solana Developer Documentation: [Web3.js](https://solana.com/docs/clients/javascript) and [Rust client](https://solana.com/docs/clients/rust) ## Interacting with Jupiter For example, to use the Jupiter Swap Aggregator Program, there are a few ways to do it: | Method | Description | | :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [Ultra Swap API](/docs/ultra) | Simply call the order endpoint to get a quote, sign then submit the transaction to the execute endpoint, we handle the rest for you, no RPCs needed! | | Flash Fill method | If you are building your own on-chain program, an alternative method from CPI, using Versioned Transaction and Address Lookup Tables, thus reducing the size of each account (used to be a limitation of using CPI method). | | [Cross Program Invocation (CPI)](https://solana.com/docs/core/cpi) | CPI method is now recommended. As of January 2025, Jupiter Swap via CPI is recommended for most users. [The `Loosen CPI restriction` feature has been deployed on Solana, you can read more here](https://github.com/solana-labs/solana/issues/26641). | ## Building Transactions Before you send a transaction to the network, you will need to build the transaction that defines the instructions to execute and accounts to read/write to. It can be complex to handle this yourself when building with Jupiter, you can [read more about it here](https://solana.com/docs/core/transactions). However, good news! The [Ultra Swap API](/docs/ultra) handles everything for you! 1. You get an order response with the encoded transaction. 2. You sign the transaction and submit to execute. 3. We handle the transaction sending and the rest for you. ## Sending Transactions Transactions on Solana can only be sent to the network through an RPC (Remote Procedure Call) endpoint. The Solana network operates with a client-server model where RPC nodes handle transactions and interact with the validators of the blockchain. We recommend using 3rd party RPC providers like [Triton](https://triton.one/) or [Helius](https://helius.dev/) for production applications. There are a few key points to note when sending transactions to the Solana network. 1. Solana transaction base fee 2. Priority fee 3. Compute units 4. Transaction broadcasting methods 5. Slippage (100% slippage will probably always work but also mean you can possibly get the worst outcome, so we need to find the balance between success optimizations and best output price) By using [Ultra Swap API](/docs/ultra), we will optimize all of these for you! * No additional params from you. * No RPCs required from you. * Simply let us handle and optimize it for you. ## About these factors ### What is Priority Fee Transactions submitted to the blockchain are prioritized based on a fee-bidding process. The higher the priority fee, the higher your transaction will be placed in the execution queue. **Overpaying Priority Fee** It is important to note that overpaying for priority fee can be detrimental in the long run. If transactions continuously outbid each other, the overall fees required to process across the network will increase over time. **Priority Fee** is an optional fee you can pay additionally to improve the chance of landing your transactions faster. * Priority Fee = **Compute Budget \* Compute Unit Price** * This is excluding the base transaction fee (5,000 lamports or 0.000005 SOL) that you always need to pay. * You not only need to outbid other transactions trying to be included in the block, but also outbid those trying to write to the same account. | Terminologies | | | :------------------ | :------------------------------------------------------------------------------ | | Global Priority Fee | The Priority Fee estimation across the entire network. | | Local Fee Market | The Priority Fee estimation when modifying a writable account (or hot account). | | Priority Fee | Compute Budget \* Compute Unit Price | | Compute Budget | How much compute unit the transaction is supposed to consume | | Compute Unit Price | Micro lamports per compute unit the transaction will use | When querying the micro-lamport per compute unit for a particular program or account, it will contain both the Global and Local Fee markets. ### What is Compute Unit Compute Unit (CU) is a standardized metric for evaluating how much "work" or "resource" is required by the transaction to execute. Different operations on Solana has varying amounts of CUs. In order to keep the blockchain efficient yet fast, each transaction, the Solana runtime has an absolute max compute unit limit of 1.4 million CU and sets a default requested max limit of 200k CU per instruction. **Set Custom Compute Unit Limit** A transaction can request a more specific and optimal compute unit limit by including a single `SetComputeUnitLimit` instruction. Either a higher or lower limit. But it may never request higher than the absolute max limit per transaction. However, we must note that higher CU also means higher Priority Fee it might need to help prioritize it. ### What are some transaction broadcasting methods 1. Typical RPCs 2. RPCs with SWQoS 3. Jito RPC ### What is Slippage A percentage or bps threshold the user specify and if the actual executed output is less than quoted output by the percentage/bps, the transaction will fail. It is more like a safeguard but the tighter threshold you go, the harder it can become to land the transaction as markets can move rapidly. # Environment Setup Source: https://dev.jup.ag/get-started/environment-setup Get Started with setting up libraries, connection and local wallet to build with Jupiter API. **ABOUT THE DOCUMENTATION** In the documentation, we are using the Solana `web3.js` library to set up connection, sign transactions, etc. ## Useful Libraries **JavaScript Libraries** * `@solana/web3.js` * `@solana/spl-token` ## Useful Scripts **Set up RPC Connection** Solana provides a [default RPC endpoint](https://solana.com/docs/core/clusters). However, as your application grows, we recommend you to always use your own or provision a 3rd party provider’s RPC endpoint such as [Helius](https://helius.dev/) or [Triton](https://triton.one/). ```bash theme={null} const connection = new Connection('https://api.mainnet-beta.solana.com'); ``` **Set up Development Wallet** You can paste in your private key for testing but this is not recommended for production. * Either use your private key in the project directly, you can do it via a `.env` file. * Or set up your private key in the [Solana CLI](https://solana.com/docs/intro/installation#create-wallet). ```javascript Store private key in .env theme={null} // In your .env file PRIVATE_KEY="" // In your index.js (or any file that needs the private key) import { Keypair } from '@solana/web3.js'; import dotenv from 'dotenv'; require('dotenv').config(); const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || ''))); ``` ```javascript Store private key in Solana CLI theme={null} import { Keypair } from '@solana/web3.js'; import fs from 'fs'; const privateKeyArray = JSON.parse(fs.readFileSync('/Path/to/.config/solana/id.json', 'utf8').trim()); const wallet = Keypair.fromSecretKey(new Uint8Array(privateKeyArray)); ``` # Get Started Source: https://dev.jup.ag/get-started/index Welcome to Jupiter’s Developer Docs. Whether you’re building your own DeFi superapp or integrating a swap into an existing application, we provide the tools and infrastructure you need to succeed. ## Product Suite Jupiter offers a comprehensive suite of products to power every aspect of your application. ### Trading & Yield Ultra is **Jupiter's Flagship Trading Solution**, the most advanced yet developer-friendly solution for building trading applications on Solana. RPC-less architecture and Jupiter handles all trading optimizations for you. Easiest way to integrate full end-to-end Ultra Swap interface. Earn, borrow and multiply Leverage trading Limit orders Dollar-cost averaging ### Data & Tools Advance heuristics-based token pricing Comprehensive token information Seamless token transfers ### Creators Token creation and LP management Token vesting ## Programs All of Jupiter's programs are deployed on the Solana Mainnet only. | Program | Address | | :----------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | **Jupiter Swap** | [`JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4`](https://solscan.io/account/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4) | | **Jupiter Referral** | [`REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3`](https://solscan.io/account/REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3) | | **Jupiter Perpetuals** | [`PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu`](https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu) | | **Jupiter Doves** | [`DoVEsk76QybCEHQGzkvYPWLQu9gzNoZZZt3TPiL597e`](https://solscan.io/account/DoVEsk76QybCEHQGzkvYPWLQu9gzNoZZZt3TPiL597e) | | **Jupiter Lend Earn** | [`jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9`](https://solscan.io/account/jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9) | | **Jupiter Lend Borrow** | [`jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi`](https://solscan.io/account/jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi) | | **Jupiter Lend Earn Rewards** | [`jup7TthsMgcR9Y3L277b8Eo9uboVSmu1utkuXHNUKar`](https://solscan.io/account/jup7TthsMgcR9Y3L277b8Eo9uboVSmu1utkuXHNUKar) | | **Jupiter Lend Liquidity** | [`jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC`](https://solscan.io/account/jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC) | | **Jupiter Lend Borrow Oracle** | [`jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc`](https://solscan.io/account/jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc) | | **Jupiter Limit Order V2** | [`j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X`](https://solscan.io/account/j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X) | | **Jupiter DCA** | [`DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M`](https://solscan.io/account/DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M) | | **Jupiter Lock** | [`LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn`](https://solscan.io/account/LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn) | | **Jupiter Governance** | [`GovaE4iu227srtG2s3tZzB4RmWBzw8sTwrCLZz7kN7rY`](https://solscan.io/account/GovaE4iu227srtG2s3tZzB4RmWBzw8sTwrCLZz7kN7rY) | | **Jupiter Voter** | [`voTpe3tHQ7AjQHMapgSue2HJFAh2cGsdokqN3XqmVSj`](https://solscan.io/account/voTpe3tHQ7AjQHMapgSue2HJFAh2cGsdokqN3XqmVSj) | ## Quick Start Paths ### Quickly Add Swap Interface to Your Application 1. [Plugin Integration](/tool-kits/plugin) - Add swap functionality in minutes - yes, as easy as that ### Build Your Own Trading Application 1. [Ultra Swap API Tutorial](/docs/ultra) - Build your first trading application 2. [Ultra Swap API Reference](/api-reference/ultra) - Dive into the complete API documentation 3. [API Key Setup](/portal/setup) - Get your API keys and scale with your demand ### Learn the Basics 1. [Environment Setup](/get-started/environment-setup) - Set up your development environment 2. [Development Basics](/get-started/development-basics) - Quick summary of Solana and Jupiter fundamentals ### Connect with Jupiter Mobile and Wallet Extension 1. [Mobile Adapter](/tool-kits/wallet-kit/jupiter-mobile-adapter) - Connect with Jupiter's mobile ecosystem 2. [Wallet Kit](/tool-kits/wallet-kit) - Comprehensive wallet integration solution # Build with Jupiter Source: https://dev.jup.ag/index export const JupiterPluginModalTrigger = ({children, className = "", style = {}}) => { const [isLoaded, setIsLoaded] = useState(false); const [error, setError] = useState(null); useEffect(() => { const checkJupiter = () => { if (window.Jupiter) { try { setIsLoaded(true); } catch (err) { setError('Failed to load Jupiter Plugin'); console.error('Jupiter Plugin availability check error:', err); } } else { setTimeout(checkJupiter, 100); } }; checkJupiter(); }, []); const launchModal = () => { if (window.Jupiter && !error) { try { window.Jupiter.init({ displayMode: "modal" }); } catch (err) { setError('Failed to initialize Jupiter Plugin modal'); console.error('Jupiter Plugin modal initialization error:', err); } } }; if (error) { return
Plugin Error
{error}
; } return ; }; export const JupiterPluginIntegrated = () => { const [isLoaded, setIsLoaded] = useState(false); const [error, setError] = useState(null); useEffect(() => { const checkJupiter = () => { if (window.Jupiter) { try { window.Jupiter.init({ displayMode: "integrated", integratedTargetId: "jupiter-plugin-integrated" }); setIsLoaded(true); } catch (err) { setError('Failed to initialize Jupiter Plugin'); console.error('Jupiter Plugin initialization error:', err); } } else { setTimeout(checkJupiter, 100); } }; checkJupiter(); }, []); if (error) { return
Plugin Loading Error
{error}
; } return
{!isLoaded &&
Loading Jupiter Plugin...
}
; };

Jupiter Plugin Demo

``` ## Step 3: Run the Project Run the project using `http-server`: ```bash theme={null} http-server ``` There you have it! You've successfully integrated Jupiter Plugin into your HTML application. * Please test the swap functionality and check the transaction. * If you require more customizations, check out the [Plugin Playground](https://plugin.jup.ag) or the [Customization](/tool-kits/plugin/customization) documentation. * If you have any questions or issues, please refer to the [FAQ](/tool-kits/plugin/faq) or contact us on [Discord](https://discord.gg/jup). # Integrate Jupiter Plugin Source: https://dev.jup.ag/tool-kits/plugin/index Seamlessly integrate end-to-end Ultra Swap functionality into any application with just a few lines of code. Jupiter Plugin is an open-source, lightweight, plug-and-play version of Jupiter Ultra Swap, allowing you to bring the exact jup.ag swap experience to any application. Try out the [Plugin Playground](https://plugin.jup.ag/) to experience the entire suite of customizations. To view the open-source code, visit the [GitHub repository](https://github.com/jup-ag/plugin). Plugin Playground { e.target.style.opacity = '1'; e.target.style.border = '2px solid #C8F284'; }} onMouseLeave={(e) => { e.target.style.opacity = '1'; e.target.style.border = '2px solid transparent'; }} data-og-width="3546" width="3546" data-og-height="2052" height="2052" data-path="static/images/plugin-playground.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=280&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=8395349bbf7f758ca18591330ca87fc7 280w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=560&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=46e08b413d1673216c5a1c4d57126c91 560w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=840&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=2914dd9f251c0f3df220e1a31e04f63c 840w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=1100&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=9d4185743a5a3991ac7e595443788fc8 1100w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=1650&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=a7ebc84c3d7e2d259f20c50d8fb2cb13 1650w, https://mintcdn.com/jupiter/mrenEfoqnubhOTHf/static/images/plugin-playground.png?w=2500&fit=max&auto=format&n=mrenEfoqnubhOTHf&q=85&s=ce6b355f81e4a7e0d9e69945e8c0a5a7 2500w" /> **QUICK START** To quick start your integration, check out the [Next.js](/tool-kits/plugin/nextjs-app-example), [React](/tool-kits/plugin/react-app-example) or [HTML](/tool-kits/plugin/html-app-example) app examples. Refer to [Customization](/tool-kits/plugin/customization) and [FAQ](/tool-kits/plugin/faq) for more information. ## Key Features * **Seamless Integration**: Embed Jupiter's swap functionality directly into your application without redirects. * **Multiple Display Options**: Choose between integrated, widget, or modal display modes. * **Customizable Options**: Configure the swap form to match your application's needs. * **RPC-less**: Integrate Plugin without any RPCs, Ultra handles transaction sending, wallet balances and token information. * **Ultra Mode**: Access to all features of Ultra Mode, read more about it in the [Ultra Swap API docs](/docs/ultra/). ## Getting Started When integrating Plugin, there are a few integration methods to think about, and choose the one that best fits your application's architecture and requirements. ### Integration Methods * **Using Window Object** - Simplest way to add and initialize Plugin. * [**Using NPM Package**](https://www.npmjs.com/package/@jup-ag/plugin) - Install via `npm install @jup-ag/plugin` and initialize as a module (will require you to maintain its dependencies). ### Wallet Integration * **Wallet Standard Support**: For applications without existing wallet provider, Plugin will provide a wallet adapter and connection - powered by [Unified Wallet Kit](/tool-kits/wallet-kit/). * **Passthrough Wallet**: For applications with existing wallet provider(s), set `enableWalletPassthrough=true` with context, and Plugin will allow the application to pass through the existing wallet provider's connection to Plugin. ### Adding Fees to plugin * **Referral Account**: You can create a referral account via [scripts](/docs/ultra/add-fees-to-ultra) or [Referral Dashboard](https://referral.jup.ag/). * **Referral Fee**: You can set the referral fee and account in the `formProps` interface when you initialize the Plugin. ### Quick Start Guides In the next sections, we'll walk you through the steps to integrate Jupiter Plugin into different types of web applications from scratch. By integrating Jupiter Plugin into your application, you can seamlessly integrate a fully functional swap interface into your application with minimal effort, while staying at the forefront of Solana DeFi innovation. # Next.js App Example Source: https://dev.jup.ag/tool-kits/plugin/nextjs-app-example In this guide, we'll walk you through from scratch the steps to integrate Jupiter Plugin into a Next.js application. ## Prerequisites Before you begin, make sure you have the following installed on your system. **Node.js and npm**: Download and install from [nodejs.org](https://nodejs.org) ## Step 1: Create a New Next.js Project Head to your preferred directory and create a new Next.js project using `create-next-app` with TypeScript template (you can use other templates or methods to start your project too): ```bash theme={null} npx create-next-app@latest plugin-demo --typescript cd plugin-demo npm run dev ``` ## Step 2: Add TypeScript Support Create a type declaration file `plugin.d.ts` in your project's `/src/types` folder: ```js theme={null} declare global { interface Window { Jupiter: JupiterPlugin; } }; export {}; ``` ```js theme={null} declare global { interface Window { Jupiter: JupiterPlugin; } } export type WidgetPosition = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'; export type WidgetSize = 'sm' | 'default'; export type SwapMode = "ExactInOrOut" | "ExactIn" | "ExactOut"; export type DEFAULT_EXPLORER = 'Solana Explorer' | 'Solscan' | 'Solana Beach' | 'SolanaFM'; export interface FormProps { swapMode?: SwapMode; initialAmount?: string; initialInputMint?: string; initialOutputMint?: string; fixedAmount?: boolean; fixedMint?: string; referralAccount?: string; referralFee?: number; } export interface IInit { localStoragePrefix?: string; formProps?: FormProps; defaultExplorer?: DEFAULT_EXPLORER; autoConnect?: boolean; displayMode?: 'modal' | 'integrated' | 'widget'; integratedTargetId?: string; widgetStyle?: { position?: WidgetPosition; size?: WidgetSize; }; containerStyles?: CSSProperties; containerClassName?: string; enableWalletPassthrough?: boolean; passthroughWalletContextState?: WalletContextState; onRequestConnectWallet?: () => void | Promise; onSwapError?: ({ error, quoteResponseMeta, }: { error?: TransactionError; quoteResponseMeta: QuoteResponse | null; }) => void; onSuccess?: ({ txid, swapResult, quoteResponseMeta, }: { txid: string; swapResult: SwapResult; quoteResponseMeta: QuoteResponse | null; }) => void; onFormUpdate?: (form: IForm) => void; onScreenUpdate?: (screen: IScreen) => void; } export interface JupiterPlugin { _instance: JSX.Element | null; init: (props: IInit) => void; resume: () => void; close: () => void; root: Root | null; enableWalletPassthrough: boolean; onRequestConnectWallet: IInit['onRequestConnectWallet']; store: ReturnType; syncProps: (props: { passthroughWalletContextState?: IInit['passthroughWalletContextState'] }) => void; onSwapError: IInit['onSwapError']; onSuccess: IInit['onSuccess']; onFormUpdate: IInit['onFormUpdate']; onScreenUpdate: IInit['onScreenUpdate']; localStoragePrefix: string; } export { }; ``` ## Step 3: Embed the Plugin Script For Next.js applications, you can add the script in two ways: ### Using App Router (Next.js 13+) In your `app/layout.tsx`: ```js theme={null} import Script from "next/script"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( ``` ## Step 4: Initialize Plugin There are two ways to initialize Jupiter Plugin in a React application: ### Method 1: Using Window Object In your `/src/App.tsx`, use the following code to initialize the plugin. ```bash theme={null} import React, { useEffect } from 'react'; import './App.css'; import './types/plugin.d'; export default function App() { useEffect(() => { // Initialize plugin window.Jupiter.init({ displayMode: "widget", integratedTargetId: "jupiter-plugin", }); }, []); return (

Jupiter Plugin Demo

); } ``` ### Method 2: Using @jup-ag/plugin Package **WARNING** Do note that using this method will require you to maintain its dependencies. Install the package: ```bash theme={null} npm install @jup-ag/plugin ``` Initialize the plugin: ```bash theme={null} import React, { useEffect } from "react"; import "@jup-ag/plugin/css"; import "./App.css"; import "./types/plugin.d"; export default function App() { useEffect(() => { import("@jup-ag/plugin").then((mod) => { const { init } = mod; init({ displayMode: "widget", integratedTargetId: "jupiter-plugin", }); }); }, []); return (

Jupiter Plugin Demo

); } ``` There you have it! You've successfully integrated Jupiter Plugin into your Next.js application. * Please test the swap functionality and check the transaction. * If you require more customizations, check out the [Plugin Playground](https://plugin.jup.ag) or the [Customization](/tool-kits/plugin/customization) documentation. * If you have any questions or issues, please refer to the [FAQ](/tool-kits/plugin/faq) or contact us on [Discord](https://discord.gg/jup). # Referral Program Source: https://dev.jup.ag/tool-kits/referral-program The Referral Program is an open-source program used by Jupiter Programs (or any other programs) to enable developers to earn fees via our APIs. **REFERRAL PROGRAM SOURCE CODE** [Open Source Repository](https://github.com/TeamRaccoons/referral): To understand and make use of the referral program better. ## Jupiter API Integrators The Jupiter Programs use the Referral Program to allow developers to earn fees when integrating with Jupiter. Below are some resources to help you quickly get started. There are a different ways to setup such as via the Jupiter Referral Dashboard or using the provided scripts. * [Jupiter Referral Dashboard](https://referral.jup.ag/): To view and manage your referral accounts used with Jupiter APIs. * [Add Fees to Ultra Swap API](/docs/ultra/add-fees-to-ultra): To add fees to your Ultra Swap API integration. * [Add Fees to Swap and Trigger API](/docs/swap/add-fees-to-swap): To add fees to your Swap and Trigger API integration. * [Add Fees to Jupiter Plugin](/tool-kits/plugin#adding-fees-to-plugin): To add fees to your Plugin integration. ## Other Program Integrators ### Project Usage If you have a project/product that runs a program on the Solana blockchain, you can integrate the Referral Program to allow/share revenue with the integrators of your program. Similar to how Jupiter Programs uses the Referral Program to help developers earn fees and/or share the revenue with Jupiter. For example, Jupiter Ultra uses the Jupiter Swap program which relies on the Referral Program. * Create a `Project` by calling `initialize_project` with your chosen `base` key and a project `name` (`base` key refers to a key identifier of your project). * Set a `default_share_bps` to share the fees with your referrers (or integrators). * An example of a `Project` account: [Jupiter Ultra Project](https://solscan.io/account/DkiqsTrw1u1bYFumumC7sCG2S8K25qc2vemJFHyW2wJc) ### Referrer Usage If you are a referrer such as a developer or integrator of a project that runs a program on the Solana blockchain, you can create the necessary accounts via the Referral Program to earn fees. * The program must be integrated with the Referral Program. * Create a `Referral` account by calling `initialize_referral_account` with the correct `Project` account, the `Referral` account, and your own `Partner` account (`Partner` account is the admin of this referral account). * Create the necessary `Referral` token accounts for the `Referral` account to receive fees in. # Integrate Wallet Kit Source: https://dev.jup.ag/tool-kits/wallet-kit/index Integrate Jupiter Wallet Kit into your application to simplify wallet connectivity across all Solana wallets, all in a unified wallet interface The Jupiter Wallet Kit is an open-source, Swiss Army Knife wallet adapter designed to streamline your development on Solana by eliminating redundancies and providing wallet adapter building blocks in a simple, plug-and-play package. This allows developers to focus on what matters most: building innovative features for your users. ## Overview * Creating a wallet notification system. * Managing wallet states (connected, disconnected, etc). * Implementing a mobile-friendly wallet connector. * Support for 20+ wallet adapters via a unified interface. * Support for all wallets built with [Solana's Wallet Standard](https://github.com/anza-xyz/wallet-standard). * Support for [Jupiter Wallet Extension](/tool-kits/wallet-kit/jupiter-wallet-extension). * Support for [Jupiter Mobile Adapter QR code login](/tool-kits/wallet-kit/jupiter-mobile-adapter). To play with different settings,features and styling. To understand and make use of the wallet adapter better. To reference code snippets and examples. ## Technical Features | Feature | Description | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------- | | **Compact Bundle** | Main ESM bundle is a lightweight 94KB (20KB gzipped). | | **Built-in Support** | Comes with Wallet Standard and Mobile Wallet Adapter support. | | **Abstracted Wallet Adapter** | Use the Bring Your Own Wallet (BYOW) approach to select custom and legacy wallets. | | **Mobile Responsive** | Designed to be mobile-first. | | **Smart Notification System** | Integrates seamlessly with your existing notification system or can be used independently. | | **Internationalization** | Supports multiple languages including English, Chinese, Vietnamese, French, Japanese, Bahasa Indonesia, and Russian. | | **Theming Options** | Choose from light, dark, and Jupiter modes, with more customization options coming soon. | | **New User Onboarding** | Simplifies the onboarding process for new users. | # Jupiter Mobile Adapter Source: https://dev.jup.ag/tool-kits/wallet-kit/jupiter-mobile-adapter Using the Jupiter Wallet Kit, we can pass in the Jupiter Mobile Adapter as a wallet in your app. The Jupiter Mobile Adapter allows you to integrate Jupiter Mobile login functionality into your app! By allowing Jupiter Mobile users to simply use the app to scan a QR code to login, they can utilize their wallets on Jupiter Mobile across any platform. ## Overview Install [@jup-ag/jup-mobile-adapter](https://www.npmjs.com/package/@jup-ag/jup-mobile-adapter) Use `useWrappedReownAdapter` (Prerequisite to create an app id on [https://dashboard.reown.com/](https://dashboard.reown.com/)) Add the `jupiterAdapter` to wallets ## Prerequisite Building on top of the example in [Jupiter Wallet Extension example](/tool-kits/wallet-kit/jupiter-wallet-extension), we will pass in the Jupiter Mobile Adapter (which uses Reown's AppKit). ## Step 1: Install dependency You will need to install the following dependency: ```bash theme={null} npm i @jup-ag/jup-mobile-adapter ``` ## Step 2: Get Reown project ID You need to input the project ID on from your [Reown's dashboard](https://dashboard.reown.com/), before you can use the Jupiter Mobile Adapter. This project ID is required for the AppKit integration that powers the mobile wallet connection functionality. To get your project ID: 1. Visit [https://dashboard.reown.com/](https://dashboard.reown.com/) 2. Sign up or log in to your account 3. Create a new project 4. Copy the project ID from your project settings (should be in the navbar) ## Step 3: Add Jupiter Mobile Adapter to wallets In your `/src/app/page.tsx` file, add the Jupiter Mobile Adapter as a wallet. ```js expandable theme={null} "use client"; import { Adapter, UnifiedWalletButton, UnifiedWalletProvider } from "@jup-ag/wallet-adapter"; import { useWrappedReownAdapter } from '@jup-ag/jup-mobile-adapter'; import { WalletNotification } from "../components/WalletNotification"; import { useMemo } from "react"; export default function Home() { // Initialize Jupiter Mobile Adapter with WalletConnect/Reown configuration // This adapter enables mobile wallet connections through WalletConnect protocol const { jupiterAdapter } = useWrappedReownAdapter({ appKitOptions: { metadata: { name: 'Jupiter Wallet Kit Demo', description: `This is a Jupiter Wallet Kit Demo with Jupiter Mobile Adapter`, url: 'https://localhost:3000', icons: ['https://jup.ag/favicon.ico'], }, projectId: '', // Get your project id from https://dashboard.reown.com/ features: { analytics: false, socials: ['google', 'x', 'apple'], email: false, }, // Disable built-in wallet list to use only Jupiter Mobile Adapter enableWallets: false, }, }); // Configure wallet adapters for the UnifiedWalletProvider // This memoized array includes the Jupiter Mobile Adapter and filters out any invalid adapters // The filter ensures each adapter has required properties (name and icon) before being used const wallets: Adapter[] = useMemo(() => { return [ jupiterAdapter, // Jupiter Mobile Adapter with WalletConnect integration ].filter((item) => item && item.name && item.icon) as Adapter[]; }, []); return (

Jupiter Mobile Adapter Demo

Connect your Solana wallet to get started

Powered by Jupiter Wallet Kit

); }; ``` There you have it! You've successfully integrated Jupiter Wallet Kit into your Next.js application and able to connect the Jupiter Mobile Adapter! * Please test the wallet functionality and build into your own application. * If you require more customizations, check out the [Wallet Kit Playground](https://wallet-kit.jup.ag) or [Github repo](https://github.com/TeamRaccoons/Unified-Wallet-Kit). * If you have any questions or issues, please reach out to us in [Discord](https://discord.gg/jup). # Jupiter Wallet Extension Source: https://dev.jup.ag/tool-kits/wallet-kit/jupiter-wallet-extension Using the Jupiter Wallet Kit, we can pass in the Jupiter Wallet Extension as a wallet in your app. ## Prerequisites Before you begin, make sure you have the following installed on your system. This is what we will be using in this example. **Node.js and npm**: Download and install from [nodejs.org](https://nodejs.org) ## Step 1: Create a new project Head to your preferred directory and create a new project using `create-next-app` with TypeScript template (you can use other templates or methods to start your project too): ```bash theme={null} npx create-next-app@latest wallet-kit-demo --typescript cd wallet-kit-demo npm run dev ``` ## Step 2: Install dependency ```bash theme={null} npm i @jup-ag/wallet-adapter ``` ## Step 3: Add notification functionality In `/src/components`, create a new file `WalletNotification.tsx` with the following content: In this example, we will use a simple browser notification system to handle the wallet connection events. ```typescript expandable theme={null} "use client"; interface IWalletNotification { publicKey?: string; shortAddress?: string; walletName?: string; metadata?: { name: string; url: string; icon: string; supportedTransactionVersions?: any; }; } // Simple notification component - you can replace this with your preferred notification library const showNotification = (message: string, type: 'success' | 'error' | 'info' = 'info') => { // For demo purposes, we'll use browser notifications // In a real app, you'd want to use a proper notification library like react-hot-toast console.log(`[${type.toUpperCase()}] ${message}`); // Simple visual feedback if (typeof window !== 'undefined') { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === 'error' ? '#ef4444' : type === 'success' ? '#10b981' : '#3b82f6'}; color: white; padding: 12px 16px; border-radius: 8px; z-index: 9999; font-family: system-ui, -apple-system, sans-serif; box-shadow: 0 4px 12px rgba(0,0,0,0.15); `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { document.body.removeChild(notification); }, 3000); } }; export const WalletNotification = { onConnect: ({ shortAddress, walletName }: IWalletNotification) => { showNotification(`Connected to ${walletName} (${shortAddress})`, 'success'); }, onConnecting: ({ walletName }: IWalletNotification) => { showNotification(`Connecting to ${walletName}...`, 'info'); }, onDisconnect: ({ walletName }: IWalletNotification) => { showNotification(`Disconnected from ${walletName}`, 'info'); }, onError: ({ walletName }: IWalletNotification) => { showNotification(`Failed to connect to ${walletName}`, 'error'); }, onNotInstalled: ({ walletName }: IWalletNotification) => { showNotification(`${walletName} is not installed`, 'error'); }, }; export default WalletNotification; ``` ## Step 4: Wrap your app in the Wallet Kit In `/src/app/page.tsx`, wrap your app with ``. This example code covers the base usage of the wallet kit which allows all wallets in your browser to be detected and used. If you have downloaded the [Jupiter Wallet Extension](https://chromewebstore.google.com/detail/iledlaeogohbilgbfhmbgkgmpplbfboh) , you should be able to see and connect to it. ```typescript expandable theme={null} "use client"; import { Adapter, UnifiedWalletButton, UnifiedWalletProvider } from "@jup-ag/wallet-adapter"; import { WalletNotification } from "../components/WalletNotification"; export default function Home() { // You can add specific wallet adapters here if needed // For now, we'll rely on the Unified Wallet Kit's built-in wallet discovery const wallets: Adapter[] = []; return (

Jupiter Wallet Extension Demo

Connect your Solana wallet to get started

Powered by Jupiter Wallet Kit

); } ``` There you have it! You've successfully integrated Jupiter Wallet Kit into your Next.js application and able to connect the Jupiter Wallet Extension! * Please test the wallet functionality and build into your own application. * If you require more customizations, check out the [Wallet Kit Playground](https://wallet-kit.jup.ag) or [Github repo](https://github.com/TeamRaccoons/Unified-Wallet-Kit). * If you have any questions or issues, please reach out to us in [Discord](https://discord.gg/jup). # Updates Source: https://dev.jup.ag/updates/index Announcements and Changes ## Ultra V3 We're excited to introduce Ultra V3, featuring major improvements on quoting and swap execution for the Ultra Swap API. * [Read the full technical deep dive in the Jupiter Developer Blog](/blog/ultra-v3). * **IronJup - Sub-Second Transaction Landing**: Transaction sending infrastructure helps reduce landing time by 50-66% compared to traditional methods and significantly reduces MEV opportunities. * **Just-In-Time Simulation to Verify Executable Price**: Rank quotes from multiple routers based on executable price by simulating quotes on-chain to verify actual executable price vs quoted price. * **Just-In-Time Market Revival**: Dynamically re-indexes markets to enable tradability for all tokens. * **Reduce Pre-Graduation Quote Latency**: Optimized pre-graduated bonding curve markets routing logic by skipping multi-route aggregation to improve quote latency from 200ms to 10ms - a 90% improvement. * **Slippage Optimization**: Automatically prioritize slippage-protected routes over purely price-optimized routes and increase volatility sensitivity for tokens with high historical volatility patterns. * **Gasless Support Coverage**: Expanded coverage to Token2022 tokens, memecoin-to-memecoin swaps (when liquidity permits), and reduced minimum trade size to \~\$10 USD. * **[Try Ultra Swap API](/docs/ultra)**: Ultra V3 features are live in the Ultra Swap API V1, try it and let us know your feedback! ## Sunsetting Legacy Endpoints We’re sunsetting several legacy endpoints over the next few weeks by gradually reducing access/rate limits. Please migrate to the latest versions ASAP. **Action Required** * Old Quote API V6: `http://quote-api.jup.ag/v6/` * Old Tokens API: `http://tokens.jup.ag` * Old Price API: `http://price.jup.ag` * Tokens V1: `http://lite-api.jup.ag/tokens/v1` * Price V2: `http://lite-api.jup.ag/price/v2` ## Deprecation of Price API V2 and Tokens API V1 [**Price API upgrades to V3**](/docs/price/v3) to support more reliable and timely pricing data - derived by the last swap price (across all transactions) and a set of heuristics to ensure the accuracy of the price and eliminate any outliers. [**Tokens API upgrades to V2**](/docs/tokens/v2) to support an easier and reliable usage with new data addition such as [Organic Score](/docs/tokens/organic-score), more trading categories like toporganicscore, and more. **Action Required** * If you are using **Price API V2** and **Tokens API V1** * Please migrate to their new versions respectively * The older versions will be deprecated by 30 September 2025 ## API Gateway: Improvements **Improved API Gateway!** For those that have been using the new hostnames at `api.jup.ag/**`, we have made improvements to the infrastructure * Reduced latency in responses and much more consistent now * Infrastructure costs reduction (will help us look into reducing costs of the plans with higher rate limits) **Dual endpoint moving forward.** We will be deploying 2 different endpoints, 1 for free usage and 1 for plans with higher rate limits via [https://portal.jup.ag/](https://portal.jup.ag/) * `api.jup.ag` will serve only pro/paid users * `lite-api.jup.ag` will be the endpoint to provide free usage **Action Required (Free plan)** * Migrate to `lite-api.jup.ag` **BY 1 MAY 2025** * The paths remain unchanged, only domain/hostname changes * The same rate limits still apply * You do not need an API Key to use the APIs for free * If you are still on `api.jup.ag` without an API key, you will get a 401 response **NO Action Required (Pro plan)** * Your usage on `api.jup.ag` remains unchanged * You can only use `api.jup.ag` with an API Key ## Trigger API: New Hostname and Breaking Changes * The `/limit/v2` path will be deprecated soon, please update your API calls to use the `/trigger/v1` path immediately. * `/execute` endpoint is introduced. * `/createOrder` endpoint now includes an additional `requestId` parameter to be used with the `/execute` endpoint. * `/cancelOrder` endpoint only builds the transaction for 1 order, while `/cancelOrders` endpoint builds the transaction for multiple orders. * The `tx` field in the responses are now `transaction` or `transactions`. * `/getTriggerOrders` endpoint is introduces a new format to get either active or historical orders (based on the query parameters). * [Please refer to the documentation for usage](/docs/trigger/create-order). | Old Paths | New Paths | | :------------------------------------------------ | :----------------------------------------------------- | | `/limit/v2/createOrder` | `/trigger/v1/createOrder` | | `/limit/v2/executeOrder` | `/trigger/v1/executeOrder` | | `/limit/v2/cancelOrder` | `/trigger/v1/cancelOrder` `/trigger/v1/cancelOrders` | | `/limit/v2/openOrders` `/limit/v2/orderHistory` | `/trigger/v1/getTriggerOrders` | ## API Gateway: New Hostnames and API Keys * API will now be served through new hostnames. * API will now be served through API keys. * API Keys will be distributed via [https://portal.jup.ag](https://portal.jup.ag) (Refer to [API Setup](/portal/setup) to get started). * Old hostnames will be slowly phased out. * Old hostnames during this period will have reduced rate limits to facilitate migration to the new API. | Service Types | Description | | :------------------------------------------------------------ | :---------------------------------------------------------- | | Free with no API key | Decreased rate limits to only accommodate for testing. | | Paid plan with API key | Fixed rate limits, self served through an API dashboard. | | Old Hostnames | New Hostnames | | :---------------------------------------- | :-------------------------------------------- | | `quote-api.jup.ag/v6/quote` | `lite-api.jup.ag/swap/v1/quote` | | `quote-api.jup.ag/v6/swap` | `lite-api.jup.ag/swap/v1/swap` | | `quote-api.jup.ag/v6/swap-instructions` | `lite-api.jup.ag/swap/v1/swap-instructions` | | `quote-api.jup.ag/v6/program-id-to-label` | `lite-api.jup.ag/swap/v1/program-id-to-label` | | `price.jup.ag/v6` | `lite-api.jup.ag/price/v2` | | `tokens.jup.ag/token/:mint` | `lite-api.jup.ag/tokens/v1/token/:mint` | | `tokens.jup.ag/tokens?tags=:tags` | `lite-api.jup.ag/tokens/v1/tagged/:tag` | | `tokens.jup.ag/tokens_with_markets` | `lite-api.jup.ag/tokens/v1/mints/tradable` |