> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solanatracker.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Swapping Tokens

> Get quotes, build swap transactions, stream live quotes, and send signed swaps on Solana with the Raptor DEX aggregator and Solana Tracker Swap API.

Raptor is Solana Tracker's DEX aggregator and swap API. A DEX aggregator checks many decentralized exchanges and finds a route for your swap. The flow is: request a quote, build a swap transaction from that quote, sign it client-side, then submit the signed transaction to Raptor's sender.

<Info>
  **HTTP Base URL:** `https://raptor-beta.solanatracker.io`\
  **WebSocket Base URL:** `wss://raptor-beta.solanatracker.io`\
  Raptor is currently in public beta.
</Info>

## Supported Routing

Raptor routes across venues and pool types including Raydium, Meteora, Orca Whirlpool, Pump.fun, Pumpswap, MoonIt, Boopfun, FluxBeam, PancakeSwap V3, and more.

Use `dexes` when you want to restrict routing to a subset of supported venues.

***

## 1. Get a Quote

Use [Get swap quote](/raptor/http/get-swap-quote) to fetch the best route. Raptor expects:

* `inputMint`
* `outputMint`
* `amount` in smallest units, which means blockchain integer amounts with no decimals
* optional `slippageBps` in basis points, where `50` means 0.5%
* optional routing controls like `dexes`, `pools`, and `maxHops`

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://raptor-beta.solanatracker.io/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000000&slippageBps=50"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    amount: "100000000",
    slippageBps: "50"
  });

  const quote = await fetch(
    `https://raptor-beta.solanatracker.io/quote?${params}`
  ).then(r => r.json());

  console.log(`Amount in: ${quote.amountIn}`);
  console.log(`Expected out: ${quote.amountOut}`);
  console.log(`Min out: ${quote.minAmountOut}`);
  console.log(`Price impact: ${(quote.priceImpact * 100).toFixed(4)}%`);
  console.log(`Route steps: ${quote.routePlan.length}`);

  for (const step of quote.routePlan) {
    console.log(`${step.dex} via ${step.pool}`);
  }
  ```

  ```python Python theme={null}
  import requests

  quote = requests.get(
      "https://raptor-beta.solanatracker.io/quote",
      params={
          "inputMint": "So11111111111111111111111111111111111111112",
          "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
          "amount": 100000000,
          "slippageBps": "50"
      }
  ).json()

  print("Expected out:", quote["amountOut"])
  print("Min out:", quote["minAmountOut"])
  print("Route count:", len(quote["routePlan"]))
  ```
</CodeGroup>

### Restrict Routes

```bash theme={null}
curl "https://raptor-beta.solanatracker.io/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000000&slippageBps=dynamic&dexes=raydium,meteora,pumpfun&maxHops=2"
```

***

## 2. Build a Swap Transaction

Use [Build swap transaction](/raptor/http/build-swap-transaction) to create a serialized transaction.

Send this request body:

* `userPublicKey`
* `quoteResponse` from `/quote`
* optional transaction tuning like `priorityFee`, `txVersion`, `computeUnitLimit`, `tipLamports`, and platform fee fields

<CodeGroup>
  ```javascript JavaScript theme={null}
  const quote = await fetch(
    "https://raptor-beta.solanatracker.io/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=100000000&slippageBps=50"
  ).then(r => r.json());

  const swap = await fetch("https://raptor-beta.solanatracker.io/swap", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      userPublicKey: "YOUR_WALLET_ADDRESS",
      wrapUnwrapSol: true,
      txVersion: "v0",
      priorityFee: "medium",
      maxPriorityFee: 1000000,
      quoteResponse: quote
    })
  }).then(r => r.json());

  console.log(`Swap tx: ${swap.swapTransaction}`);
  console.log(`Last valid block height: ${swap.lastValidBlockHeight}`);
  console.log(`Priority fee: ${swap.prioritizationFeeLamports}`);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://raptor-beta.solanatracker.io/swap" \
    -H "Content-Type: application/json" \
    -d '{
      "userPublicKey": "YOUR_WALLET_ADDRESS",
      "wrapUnwrapSol": true,
      "txVersion": "v0",
      "priorityFee": "medium",
      "quoteResponse": {
        "inputMint": "So11111111111111111111111111111111111111112",
        "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "amountIn": "100000000",
        "amountOut": "14321000",
        "minAmountOut": "14249400",
        "feeAmount": "25000",
        "priceImpact": 0.0008,
        "slippageBps": 50,
        "routePlan": [],
        "contextSlot": 314159265,
        "timeTaken": 0.012
      }
    }'
  ```
</CodeGroup>

### Transaction Controls

| Field                                                  | Purpose                                                                 |
| ------------------------------------------------------ | ----------------------------------------------------------------------- |
| `txVersion`                                            | `legacy` or `v0`                                                        |
| `priorityFee`                                          | priority level like `medium`, `high`, `veryHigh` or exact microlamports |
| `maxPriorityFee`                                       | cap dynamic priority fees                                               |
| `computeUnitPriceMicroLamports`                        | Priority fee: override compute-unit price directly                      |
| `computeUnitLimit`                                     | override CU limit                                                       |
| `tipAccount` / `tipLamports`                           | add optional SOL tip                                                    |
| `feeAccount` / `feeBps` / `feeFromInput` / `chargeBps` | platform fee controls                                                   |

### Token accounts and SOL wrapping

The `/swap` response is a ready-to-sign transaction that already includes everything needed for the swap to land on-chain:

* **Associated Token Accounts (ATAs)** — Raptor adds `createAssociatedTokenAccountIdempotent` instructions to `setupInstructions` for any input, output, or intermediate mint where the user does not already have an ATA. You do not need to pre-create ATAs for the output token; the rent (\~0.00203 SOL per account) is paid by `userPublicKey`. If you build your own transaction from `/swap-instructions`, keep the `setupInstructions` and `cleanupInstruction` arrays — that is where ATA creation and SOL wrap/unwrap live.
* **SOL wrapping** — `wrapUnwrapSol` defaults to `true`. Raptor wraps SOL into wSOL in `setupInstructions` when SOL is the input, and unwraps any remaining wSOL back to native SOL in `cleanupInstruction` when SOL is the output. Set `wrapUnwrapSol: false` only if you are managing a long-lived wSOL account yourself.

***

## 3. Quote and Build in One Request

Use [Quote and swap in one request](/raptor/http/quote-and-swap-in-one-request) if you want a single request instead of separate `/quote` and `/swap` calls.

```javascript theme={null}
const result = await fetch("https://raptor-beta.solanatracker.io/quote-and-swap", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    userPublicKey: "YOUR_WALLET_ADDRESS",
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    amount: 100000000,
    slippageBps: "dynamic",
    txVersion: "v0",
    priorityFee: "high",
    maxHops: 2,
    dexes: "raydium,meteora,whirlpool"
  })
}).then(r => r.json());

console.log(result.quote.amountOut);
console.log(result.swapTransaction);
```

***

## 4. Sign Client-Side

Raptor returns `swapTransaction` as base64. Sign it in the wallet, then send the signed base64 transaction.

```javascript theme={null}
import { VersionedTransaction } from "@solana/web3.js";

const txBytes = Uint8Array.from(atob(swap.swapTransaction), c => c.charCodeAt(0));
const tx = VersionedTransaction.deserialize(txBytes);

const signed = await wallet.signTransaction(tx);
const signedBase64 = btoa(String.fromCharCode(...signed.serialize()));
```

***

## 5. Send the Signed Transaction

Send signed transactions with [raptor/transactions](/raptor/transactions): `POST /send-transaction`.

```javascript theme={null}
const sendResult = await fetch("https://raptor-beta.solanatracker.io/send-transaction", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ transaction: signedBase64 })
}).then(r => r.json());

console.log(`Signature: ${sendResult.signature}`);
console.log(`Accepted: ${sendResult.success}`);
```

Raptor sends through Yellowstone Jet TPU and retries in the background until the transaction confirms or expires.

### Track Status

```javascript theme={null}
const status = await fetch(
  `https://raptor-beta.solanatracker.io/transaction/${sendResult.signature}`
).then(r => r.json());

console.log(`Status: ${status.status}`);
console.log(`Latency: ${status.latency_ms}ms`);
console.log(`Slot: ${status.slot}`);
```

Valid statuses are `pending`, `confirmed`, `failed`, and `expired`.

***

## 6. Build Instructions Only

Use [Build swap instructions](/raptor/http/build-swap-instructions) when you want instructions without a transaction wrapper.

```javascript theme={null}
const instructions = await fetch("https://raptor-beta.solanatracker.io/swap-instructions", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    userPublicKey: "YOUR_WALLET_ADDRESS",
    quoteResponse: quote,
    txVersion: "v0"
  })
}).then(r => r.json());

console.log(instructions.swapInstruction.programId);
console.log(instructions.addressLookupTableAddresses);
```

### Atomic Buy → Sell in One Transaction (Token Ledger)

When you want to **buy a token on one pool and sell it on another in the same transaction** — for example, a two-hop arbitrage — you do not know the exact amount the sell leg will receive until the buy leg executes on-chain. The `tokenLedgerInstruction` returned by `/swap-instructions` solves this: it records the actual output of the buy leg at runtime, so the sell leg consumes whatever the buy produced.

The flow is:

1. Call `/swap-instructions` for the **buy leg** (e.g. SOL → TOKEN on pool 1). Use the returned `tokenLedgerInstruction` to capture the on-chain output amount.
2. Call `/swap-instructions` for the **sell leg** (e.g. TOKEN → SOL on pool 2). The sell instruction reads its input amount from the token ledger written by the buy leg.
3. Assemble both calls' `computeBudgetInstructions`, `setupInstructions`, `tokenLedgerInstruction`, `swapInstruction`, and `cleanupInstruction` into a single `VersionedTransaction`. Deduplicate setup/cleanup instructions and combine the `addressLookupTableAddresses` from both responses.
4. Sign once and submit through `/send-transaction`.

```javascript theme={null}
// 1. Buy leg: SOL -> TOKEN on pool 1
const buy = await fetch("https://raptor-beta.solanatracker.io/swap-instructions", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    userPublicKey: WALLET,
    quoteResponse: buyQuote,     // from /quote restricted to pool 1
    txVersion: "v0"
  })
}).then(r => r.json());

// 2. Sell leg: TOKEN -> SOL on pool 2.
//    amountIn in sellQuote is a placeholder — the ledger overrides it at runtime.
const sell = await fetch("https://raptor-beta.solanatracker.io/swap-instructions", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    userPublicKey: WALLET,
    quoteResponse: sellQuote,    // from /quote restricted to pool 2
    txVersion: "v0"
  })
}).then(r => r.json());

// 3. Assemble: compute budget + setup + tokenLedger + buy swap + sell swap + cleanup
const instructions = [
  ...buy.computeBudgetInstructions,
  ...dedupe([...buy.setupInstructions, ...sell.setupInstructions]),
  buy.tokenLedgerInstruction,    // present when the sell leg needs it
  buy.swapInstruction,
  sell.swapInstruction,
  sell.cleanupInstruction
].filter(Boolean);

const lookupTables = [
  ...buy.addressLookupTableAddresses,
  ...sell.addressLookupTableAddresses
];
```

**Notes**

* `tokenLedgerInstruction` is `null` for ordinary single-swap responses — that is expected. It is populated when the swap is part of a chain where a later instruction needs to read the runtime output of an earlier one.
* `amountOut` in a quote is the expected output and cannot be set to `auto`. The token ledger is the supported way to chain "sell whatever the buy produced" without knowing the amount ahead of time.
* Keep both legs inside the compute unit budget. You may need to raise `computeUnitLimit` and `computeUnitPriceMicroLamports` when combining two swaps.

***

## 7. Stream Live Quotes

Use the Raptor WebSocket `/stream` endpoint for quote updates.

```javascript theme={null}
const ws = new WebSocket("wss://raptor-beta.solanatracker.io/stream");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "subscribe",
    id: "sol-usdc-quote",
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    amount: 100000000,
    slippageBps: "50",
    maxHops: 2,
    dexes: "raydium,meteora,whirlpool"
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "subscribed") {
    console.log(`Subscribed: ${msg.id}`);
  }

  if (msg.type === "quote") {
    console.log(`Quote update: ${msg.data.amountOut}`);
  }
};
```

***

## 8. Stream Ready-to-Sign Swap Transactions

Use `/stream/swap` when you want live quote updates plus a fresh prebuilt transaction.

This stream requires `userPublicKey` because Raptor builds the transaction for that wallet.

```javascript theme={null}
const ws = new WebSocket("wss://raptor-beta.solanatracker.io/stream/swap");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "subscribe",
    id: "sol-usdc-swap",
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    amount: 100000000,
    userPublicKey: "YOUR_WALLET_ADDRESS",
    slippageBps: "50",
    txVersion: "v0",
    priorityFee: "medium",
    wrapUnwrapSol: true,
    maxHops: 2
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "swap") {
    console.log(`Updated quote: ${msg.quote.amountOut}`);
    console.log(`Swap tx: ${msg.swapTransaction}`);
    console.log(`Last valid block height: ${msg.lastValidBlockHeight}`);
  }
};
```

`/stream/swap` re-sends the latest transaction after 10 slots without an update to help avoid transaction expiry.

***

## End-to-End Flow

1. Call `/quote` with `inputMint`, `outputMint`, `amount`, and optional routing controls.
2. Pass the returned `quoteResponse` into `/swap` with `userPublicKey`.
3. Sign the returned `swapTransaction` in the wallet.
4. Submit the signed base64 transaction to `/send-transaction`.
5. Poll `/transaction/{signature}` for status.

If you want fewer round-trips, use `/quote-and-swap` instead of separate `/quote` and `/swap` calls.

<CardGroup cols={2}>
  <Card title="Raptor Overview" href="/raptor/overview">
    Full endpoint reference, supported DEXs, routing controls, and WebSocket details.
  </Card>

  <Card title="Raptor Transactions" href="/raptor/transactions">
    Send signed transactions and track confirmation status through Raptor.
  </Card>
</CardGroup>
