A Raydium swap is an on-chain Solana transaction that exchanges one SPL token for another against liquidity held in Raydium pools, with routing computed across AMM v4, CPMM and CLMM pool types. You can execute one through the hosted Raydium interface, through the Raydium Trade API, or by building instructions directly with the SDK. This article explains the pool mechanics, walks through a quote-build-sign-send implementation, shows how to verify the result on-chain, and covers the failure modes that most often break swaps: stale quotes, slippage, priority fees, and RPC reliability. It separates documented protocol behavior from the measurements you should run yourself.
What a Raydium swap actually executes
A Raydium swap is not a single contract call to one pool. It is a Solana transaction that contains one or more swap instructions, each targeting a specific pool program, plus the associated token account setup and any wrapping or unwrapping of SOL. Raydium's routing layer decides which pools to touch and in what order so that the input token reaches the output token at the best available price across the liquidity it can see.
There are three pool families you will encounter. Legacy AMM v4 pools use a constant-product curve with an order-book-adjacent design from Raydium's early architecture. CPMM pools are constant-product pools with a simpler, permissionless creation path. CLMM pools are concentrated-liquidity pools where liquidity is placed in price ranges, which can produce much tighter spreads but also different price-impact behavior near range boundaries. A swap route may cross any combination of these.
The practical consequence is that you rarely need to know which pool a pair trades in. What you do need to control is the quote you accept, the slippage tolerance you sign with, and the compute budget and priority fee that determine whether the transaction lands before the quote goes stale.
- Input and output mints, plus the wallet's associated token accounts
- A route across one or more pools, each with its own program
- A minimum-output constraint derived from your slippage setting
- A compute unit limit and priority fee for landing
- A recent blockhash that expires within roughly 60-90 seconds
Assumptions and prerequisites before you start
This guide assumes you are building against Solana mainnet with a funded wallet and a dedicated RPC endpoint you control. Public endpoints rate-limit aggressively and will cause intermittent failures that look like swap bugs but are transport problems. If you have not chosen an endpoint yet, the RPC Assistant comparison of Solana RPC providers covers the tradeoffs, and the Solana devnet guide is the right place to rehearse the flow without spending real funds.
You need Node.js 18 or newer for the global fetch API, a Solana keypair, and a small SOL balance to cover transaction fees, priority fees, and any rent for associated token accounts. For programmatic swaps you will use either the Raydium Trade API, which quotes and builds transactions for you, or the current Raydium SDK v2 if you need direct on-chain control. The archived v1 SDK predates CPMM and CLMM support and should not be used for new work.
- A Solana RPC_URL you control, ideally with a fallback
- A PRIVATE_KEY or wallet adapter for signing
- SOL for fees and, if swapping from SOL, for wrapping
- A slippage policy expressed in basis points
- A priority-fee strategy appropriate to current network conditions
The quote, build, sign, send mental model
Every Raydium swap, whether you click a button in the interface or call an API, follows the same four-stage lifecycle. First, a quote is computed from current pool state: given an input amount and mints, what output amount is achievable and along which route. Second, a transaction is built that encodes that route with your wallet as the payer and your slippage as the minimum-output guard. Third, you sign it locally so the private key never leaves your environment. Fourth, you send it to an RPC node, which broadcasts it to the cluster.
The critical insight is that stages one and two are advisory and stage four is authoritative. The quote reflects pool state at the moment it was computed. Between quote and confirmation, other traders move the pools, so the on-chain result can differ from the quote. Your slippage tolerance is the contract between the two: it tells the program how much worse than the quoted output you will accept before the transaction reverts. Setting it too tight causes failures in volatile markets; setting it too loose exposes you to sandwich-style price impact.
Fetching a quote and building a swap with the Trade API
The Raydium Trade API exposes a compute endpoint that returns a quote and a serialized transaction in one call. The example below uses placeholders for the RPC endpoint and keypair; substitute your own values. It swaps a fixed lamport amount of SOL for USDC, sets a 50 basis point slippage tolerance, and requests a priority fee. Treat the returned transaction as opaque: sign it, send it, and confirm it rather than trying to parse its instructions.
import { Connection, Keypair, VersionedTransaction } from '@solana/web3.js';
import bs58 from 'bs58';
const RPC_URL = process.env.RPC_URL!;
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY!));
const connection = new Connection(RPC_URL, 'confirmed');
const WSOL = 'So11111111111111111111111111111111111111112';
const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
async function swapSolToUsdc(lamportsIn: number) {
const url = new URL('https://transaction-v1.raydium.io/compute/swap-base-in');
url.searchParams.set('inputMint', WSOL);
url.searchParams.set('outputMint', USDC);
url.searchParams.set('amount', String(lamportsIn));
url.searchParams.set('slippageBps', '50');
url.searchParams.set('txVersion', 'V0');
url.searchParams.set('wallet', wallet.publicKey.toBase58());
const quote = await (await fetch(url)).json();
if (!quote.success) throw new Error(JSON.stringify(quote));
const tx = VersionedTransaction.deserialize(
Buffer.from(quote.data.transaction, 'base64')
);
tx.sign([wallet]);
const signature = await connection.sendTransaction(tx, {
maxRetries: 3,
skipPreflight: false,
});
const latest = await connection.getLatestBlockhash('confirmed');
const result = await connection.confirmTransaction(
{ signature, ...latest },
'confirmed'
);
return { signature, result, quote: quote.data };
}
swapSolToUsdc(10_000_000).then(console.log).catch(console.error);Verifying the swap result on-chain
A confirmed signature is not proof that you received the expected amount. Always read the transaction metadata and compare the actual token balance changes against the quote. The snippet below fetches the parsed transaction and prints the pre and post balances for the wallet's token accounts, which is the ground truth for what moved.
async function inspectSwap(signature: string) {
const tx = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
commitment: 'confirmed',
});
if (!tx) throw new Error('transaction not found');
const pre = tx.meta!.preTokenBalances ?? [];
const post = tx.meta!.postTokenBalances ?? [];
const owner = wallet.publicKey.toBase58();
const rows = post
.filter((b) => b.owner === owner)
.map((b) => {
const before = pre.find((p) => p.accountIndex === b.accountIndex);
const delta =
Number(b.uiTokenAmount.uiAmountString) -
Number(before?.uiTokenAmount.uiAmountString ?? 0);
return { mint: b.mint, delta };
});
console.table(rows);
console.log('fee lamports:', tx.meta!.fee);
console.log('compute units:', tx.meta!.computeUnitsConsumed);
}
inspectSwap('<SIGNATURE>');Recording swap measurements you can compare
Because pool state and network conditions change constantly, any single swap tells you little. To evaluate a routing strategy, a priority-fee policy, or an RPC provider, run a controlled series and record the same fields each time. The table below is a template; fill it with your own observed values rather than relying on anyone else's numbers.
| Run | Input mint | Output mint | Quoted out | Actual out | Slippage bps | Priority fee (µLamports/CU) | CU consumed | Land time (ms) | Status |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SOL | USDC | 50 | ||||||
| 2 | SOL | USDC | 50 | ||||||
| 3 | USDC | RAY | 100 |
Run at least ten iterations per configuration and report the median and the failure rate, not the best case. Land time should be measured from send to confirmed commitment using a monotonic clock. If you are comparing RPC endpoints, hold the wallet, amount, and slippage constant and vary only the endpoint so the comparison is meaningful.
Troubleshooting swaps that fail or underdeliver
Most swap failures fall into a small number of categories, and each has a distinct signature in the transaction logs. Reading the logs before changing anything will save you from chasing the wrong fix.
Slippage exceeded is the most common. The program returns an error indicating the minimum output constraint was violated, which means the pool moved beyond your tolerance between quote and execution. Widen slippage modestly, reduce trade size, or re-quote immediately before sending. Blockhash not found means your transaction expired before landing; fetch a fresh blockhash and retry, and investigate why the send was slow. Insufficient funds for rent appears when the wallet lacks SOL to create an associated token account for a token it has never held. Compute budget exceeded means the route needed more compute units than the limit; raise the limit rather than the priority fee.
Timeouts and 429 responses from the RPC layer are transport failures, not swap failures. They cluster on shared public endpoints and disappear when you move to a dedicated node. If you see them repeatedly, the fix is infrastructure, not transaction parameters.
- Slippage exceeded: re-quote, widen tolerance, or reduce size
- Blockhash not found: refresh blockhash and retry promptly
- Insufficient funds for rent: fund the wallet with SOL
- Compute budget exceeded: raise the CU limit
- 429 or timeout: move to a dedicated RPC endpoint
Limitations and tradeoffs of each swap approach
The hosted interface is the fastest path for a human trader but offers no programmatic control and depends on a frontend you do not operate. The Trade API is the simplest programmatic route and handles routing across pool types for you, but it introduces a dependency on a hosted service and gives you limited visibility into route construction. The SDK v2 gives you direct control over instructions and pool selection, which matters for market making, arbitrage, and custom fee logic, at the cost of significantly more code and responsibility for route selection.
CLMM pools deserve separate consideration. Concentrated liquidity can offer better prices within a range, but price impact near range edges can be discontinuous, and a route that looks cheap at quote time can degrade quickly. For large swaps, splitting across routes or using a limit-style approach may be preferable to a single market order. None of these approaches eliminate MEV exposure; priority fees and slippage settings change the economics but do not remove the risk.
Operational hardening for production swap services
If you are running swaps as a service rather than as a one-off, the reliability of your RPC layer dominates everything else. A dedicated Solana node removes the noisy-neighbor problem of shared endpoints and gives you consistent sendTransaction behavior under load. Pair it with a fallback endpoint and a retry policy that distinguishes retryable transport errors from terminal program errors. Instrument every swap with the fields from the measurement table so you can detect regressions in land time or failure rate before your users do.
For teams operating at scale, a dedicated node with predictable throughput is usually cheaper than the engineering time spent debugging intermittent failures on shared infrastructure. Review the dedicated node options and the RPC pricing model to size the commitment against your expected transaction volume.
Next steps from a working swap to a reliable one
Once a single swap succeeds end to end, the work shifts from correctness to reliability and cost. Rehearse the full flow on devnet, then run the measurement series on mainnet with small amounts before scaling up. Compare your observed land times and failure rates across at least two RPC configurations so you know which variable actually matters. If you are still selecting an endpoint, start with the Solana RPC provider comparison and the broader RPC endpoints guide, then validate your choice with your own measurements rather than marketing claims.
From there, explore the network directory for other chains you may need to support, and consider whether your swap service should quote and build locally with the SDK for lower latency. The consistent theme is that Raydium swap performance is a function of pool state, transaction parameters, and RPC quality together; optimizing only one of the three leaves the other two as your bottleneck.