◈   ⌘ api · Intermediate

Jupiter Swap API with Python: A Trader's Guide

Learn how to use Jupiter Swap API with Python to execute Solana DEX trades programmatically, from setup to live swaps with real code examples.

Uncle Solieditor · voc · 06.05.2026 ·views 26
◈   Contents
  1. → What Jupiter Swap API Actually Does
  2. → Setting Up Your Python Environment
  3. → Fetching a Swap Quote
  4. → Building and Submitting the Swap Transaction
  5. → Error Handling and Production-Ready Patterns
  6. → Practical Considerations Before Going Live
  7. → Frequently Asked Questions
  8. → Putting It All Together

Jupiter is the leading DEX aggregator on Solana, routing swaps across dozens of liquidity pools to get you the best possible price. If you've ever executed a trade on Phantom wallet or Solflare and wondered how it finds that optimal route — that's Jupiter under the hood. Now, with the Jupiter Swap API, you can plug directly into that same routing engine using Python, bypassing any UI and automating your trades with precision.

This is meaningfully different from trading on centralized exchanges like Binance or Bybit. There's no order book, no account registration, no API key to generate from a dashboard. You interact directly with the Solana blockchain through a publicly available REST API. Your wallet signs the transaction locally, and Jupiter routes it to the best available liquidity — all in one flow.

What Jupiter Swap API Actually Does

Jupiter's API is a two-step process: first you fetch a quote, then you build and submit a transaction. The quote endpoint scans all available Solana DEXes — Orca, Raydium, Meteora, Lifinity and others — and returns the optimal swap route. The second endpoint takes that route and returns a serialized transaction ready for your wallet to sign.

Unlike platforms like Coinbase or OKX where the exchange holds custody and executes on your behalf, everything here is non-custodial. Your private key never leaves your machine. The API just provides the routing logic and transaction structure — you do the signing.

Setting Up Your Python Environment

You'll need a few libraries: requests for HTTP calls, solders or solana-py for transaction signing and RPC submission, and base64 for deserializing the transaction. Keep your private key in an environment variable — never hardcode it.

pip install requests solders base58 python-dotenv
import os
import requests
import base64
from dotenv import load_dotenv
from solders.keypair import Keypair
from solders.transaction import VersionedTransaction
from solana.rpc.api import Client

load_dotenv()

# Load wallet from env — never hardcode private keys
RAW_KEY = os.environ["SOLANA_PRIVATE_KEY"]  # base58 encoded private key
keypair = Keypair.from_base58_string(RAW_KEY)

# Solana RPC endpoint (use a reliable one like Helius or QuickNode in production)
RPC_URL = os.environ.get("SOLANA_RPC_URL", "https://api.mainnet-beta.solana.com")
client = Client(RPC_URL)

JUPITER_QUOTE_URL = "https://quote-api.jup.ag/v6/quote"
JUPITER_SWAP_URL  = "https://quote-api.jup.ag/v6/swap"

print(f"Wallet loaded: {keypair.pubkey()}")
Use a dedicated trading wallet with only the funds you need for swaps. Never use your main wallet's private key in a script. Keep the key in .env and add .env to .gitignore before committing anything.

Fetching a Swap Quote

The quote endpoint needs three required parameters: inputMint (the token you're selling), outputMint (the token you're buying), and amount (in the token's smallest unit — lamports for SOL, which has 9 decimals, or base units for USDC, which has 6). Slippage tolerance is optional but you should always set it.

def get_quote(input_mint: str, output_mint: str, amount: int, slippage_bps: int = 50) -> dict:
    """
    Fetch best swap route from Jupiter.
    slippage_bps: 50 = 0.5%, 100 = 1%
    """
    params = {
        "inputMint": input_mint,
        "outputMint": output_mint,
        "amount": amount,
        "slippageBps": slippage_bps,
        "onlyDirectRoutes": False,  # allow multi-hop for better price
        "asLegacyTransaction": False
    }

    resp = requests.get(JUPITER_QUOTE_URL, params=params, timeout=10)
    resp.raise_for_status()
    quote = resp.json()

    in_amount  = int(quote["inAmount"]) / 1e6   # USDC has 6 decimals
    out_amount = int(quote["outAmount"]) / 1e9  # SOL has 9 decimals
    price_impact = float(quote["priceImpactPct"])

    print(f"Swap: {in_amount:.2f} USDC → {out_amount:.6f} SOL")
    print(f"Price impact: {price_impact:.4f}%")
    print(f"Route: {' → '.join([r['swapInfo']['label'] for r in quote['routePlan']])}")

    return quote

# Example: swap 10 USDC for SOL
USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
SOL_MINT  = "So11111111111111111111111111111111111111112"

quote = get_quote(
    input_mint=USDC_MINT,
    output_mint=SOL_MINT,
    amount=10_000_000,  # 10 USDC (6 decimals)
    slippage_bps=50
)

Notice the price impact field — this is critical for larger trades. On a DEX like Jupiter you're trading against automated market makers, not order books like on Bybit or Gate.io. A 10 USDC swap will have near-zero impact, but 50,000 USDC can move the price meaningfully depending on pool depth. Always check this before executing.

Building and Submitting the Swap Transaction

Once you have a quote, pass it to the swap endpoint to get a serialized transaction. You deserialize it, sign it with your keypair, and send it to Solana's RPC. Jupiter returns a versioned transaction by default, which supports address lookup tables for efficiency.

def execute_swap(quote: dict, keypair: Keypair, client: Client) -> str:
    """
    Build transaction from quote, sign it, and submit to Solana.
    Returns transaction signature.
    """
    payload = {
        "quoteResponse": quote,
        "userPublicKey": str(keypair.pubkey()),
        "wrapAndUnwrapSol": True,   # auto-wrap SOL to wSOL and back
        "dynamicComputeUnitLimit": True,  # let Jupiter estimate CU
        "prioritizationFeeLamports": "auto"  # auto priority fee
    }

    resp = requests.post(JUPITER_SWAP_URL, json=payload, timeout=15)
    resp.raise_for_status()
    swap_data = resp.json()

    # Deserialize the transaction
    raw_tx = base64.b64decode(swap_data["swapTransaction"])
    tx = VersionedTransaction.from_bytes(raw_tx)

    # Sign with our wallet
    tx.sign([keypair])

    # Submit to Solana
    raw_signed = bytes(tx)
    result = client.send_raw_transaction(
        raw_signed,
        opts={"skip_preflight": False, "preflight_commitment": "confirmed"}
    )

    sig = str(result.value)
    print(f"Transaction submitted: https://solscan.io/tx/{sig}")
    return sig


def wait_for_confirmation(client: Client, signature: str, max_retries: int = 30) -> bool:
    """Poll for transaction confirmation."""
    import time
    for attempt in range(max_retries):
        status = client.get_signature_statuses([signature])
        info = status.value[0]
        if info is not None:
            if info.err:
                print(f"Transaction failed: {info.err}")
                return False
            if info.confirmation_status in ("confirmed", "finalized"):
                print(f"Confirmed after {attempt + 1} polls")
                return True
        time.sleep(1)
    print("Timed out waiting for confirmation")
    return False


# Execute the swap we quoted above
sig = execute_swap(quote, keypair, client)
confirmed = wait_for_confirmation(client, sig)
Set prioritizationFeeLamports to 'auto' during high network congestion — Solana can get crowded and under-priced transactions get dropped. In calm markets, a fixed 5000 lamports is usually fine and cheaper.

Error Handling and Production-Ready Patterns

Raw API calls will fail eventually — network timeouts, stale quotes, slippage exceeded, insufficient SOL for fees. A production bot needs to handle all of these gracefully. Here's a pattern that wraps the full flow with retry logic and meaningful error messages.

import time
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("jup_bot")


def safe_swap(input_mint: str, output_mint: str, amount: int,
              slippage_bps: int = 50, max_retries: int = 3) -> str | None:
    """Full swap flow with retry and error handling."""

    for attempt in range(1, max_retries + 1):
        try:
            log.info(f"Attempt {attempt}: fetching quote...")
            quote = get_quote(input_mint, output_mint, amount, slippage_bps)

            # Reject if price impact is too high
            impact = float(quote["priceImpactPct"])
            if impact > 1.0:
                log.warning(f"Price impact {impact:.2f}% exceeds threshold — aborting")
                return None

            log.info("Executing swap...")
            sig = execute_swap(quote, keypair, client)

            confirmed = wait_for_confirmation(client, sig)
            if confirmed:
                log.info(f"Swap completed: {sig}")
                return sig
            else:
                log.warning("Transaction not confirmed, retrying...")

        except requests.exceptions.HTTPError as e:
            log.error(f"HTTP error: {e.response.status_code} — {e.response.text[:200]}")
        except requests.exceptions.Timeout:
            log.error("Request timed out")
        except Exception as e:
            log.error(f"Unexpected error: {e}")

        if attempt < max_retries:
            time.sleep(2 ** attempt)  # exponential backoff

    log.error("All retry attempts failed")
    return None


# Example usage with guard
if __name__ == "__main__":
    result = safe_swap(
        input_mint=USDC_MINT,
        output_mint=SOL_MINT,
        amount=10_000_000,
        slippage_bps=50
    )
    if result:
        print(f"Success: {result}")
    else:
        print("Swap failed — check logs")

This same pattern can be extended into a signal-driven bot. Platforms like VoiceOfChain emit real-time trading signals for Solana tokens — when a signal fires, your bot fetches a fresh quote and executes the swap automatically. The key is that quotes are only valid for about 30 seconds, so never cache them; always fetch a new one right before you intend to submit.

Practical Considerations Before Going Live

Trading on Jupiter is fundamentally different from operating on centralized exchanges like KuCoin or Bitget. There's no cancel button once a transaction lands on-chain. Slippage settings are your only protection against bad fills. Here's what matters before you run real money through a bot:

CEX vs Jupiter DEX — Key Differences for Bot Traders
FactorCentralized (Binance, Bybit)Jupiter DEX
CustodyExchange holds fundsYour wallet, non-custodial
Order typesLimit, market, stop-lossMarket only (slippage = your limit)
CancellationCancel anytime before fillIrreversible once submitted
LatencyMilliseconds via WebSocketSolana block time ~400ms
FeesMaker/taker 0.1%DEX fee + priority fee + routing
LiquidityDeep order booksAMM pools — depth varies by token

Frequently Asked Questions

Do I need an API key to use Jupiter Swap API?
No. Jupiter's quote and swap endpoints are publicly accessible with no authentication required. You just make HTTP requests to quote-api.jup.ag. Rate limits are generous for individual traders, though heavy production use benefits from their paid tier.
What's the difference between Jupiter and trading on Binance via API?
On Binance you authenticate with API keys and the exchange executes trades from your account balance it holds. Jupiter is non-custodial — your Python script signs the transaction locally with your private key and submits it directly to Solana. No account, no KYC, no custody.
Why does my transaction fail with 'slippage tolerance exceeded'?
The market moved between when you fetched the quote and when your transaction landed. Either increase your slippage tolerance (try 100 bps instead of 50) or reduce the delay between fetching the quote and submitting. Quotes are only valid for roughly 30 seconds.
Can I execute limit orders instead of market swaps?
Jupiter does offer a limit order API (separate from the swap API) that lets you set a target price and wait for execution. However, it requires keeping your transaction open on-chain. Most Python bots combine market swaps with off-chain price checking logic to simulate limit order behavior.
How do I find the mint address for a specific token?
Use Jupiter's token list API at quote-api.jup.ag/v6/tokens or search on Solscan.io or Birdeye.so by token name. The mint address is a base58 string unique to each SPL token — SOL's wrapped mint is So11111111111111111111111111111111111111112 and USDC is EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.
Is it safe to run this bot 24/7 unattended?
Only with proper safeguards: cap the maximum trade size in code, set a daily loss limit that halts the bot, monitor your wallet balance externally, and use a dedicated wallet with limited funds. Never run a bot with your main wallet's private key or more capital than you can afford to lose to a bug.

Putting It All Together

The Jupiter Swap API with Python gives you direct, non-custodial access to Solana's best DEX liquidity in under 100 lines of code. The flow is clean: fetch a quote, check price impact and slippage, build the transaction, sign locally, submit. No exchange accounts, no KYC, no withdrawal limits like you'd face on OKX or Coinbase.

The real power comes when you chain this with a signal source. VoiceOfChain provides real-time on-chain trading signals that can trigger your swap logic automatically — catching momentum moves before they're reflected on centralized exchanges. Your bot becomes a pipeline: signal fires → quote fetched → slippage checked → transaction submitted, all within seconds of the on-chain event.

Start small, verify every step with print statements and Solscan transaction links, and scale only after you've confirmed the full flow works reliably. The code above is production-ready as a foundation — extend it with your own position sizing, signal filters, and risk rules.

◈   more on this topic
◉ basics Mastering the ccxt library documentation for crypto traders ⌂ exchanges Mastering the Binance CCXT Library for Crypto Traders ⌬ bots Best Crypto Trading Bots 2025: Profitable AI-Powered Strategies