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

# Get Swap Instructions

> Get raw swap instructions and build your own transaction

The `/build` endpoint returns raw swap instructions instead of an assembled transaction. You build the transaction yourself, which gives you full control to add custom instructions, CPI, or modify any part of the transaction.

## Quick start

### Prerequisites

<Accordion title="Imports, types, and helpers">
  <CodeGroup>
    ```typescript @solana/kit expandable theme={null}
    import {
      AccountRole,
      Address,
      address,
      AddressesByLookupTableAddress,
      appendTransactionMessageInstructions,
      Base64EncodedBytes,
      Blockhash,
      compileTransaction,
      compressTransactionMessageUsingAddressLookupTables,
      createKeyPairSignerFromBytes,
      createSolanaRpc,
      createTransactionMessage,
      getBase58Decoder,
      getBase58Encoder,
      getBase64Codec,
      getBase64EncodedWireTransaction,
      AccountMeta,
      Instruction,
      pipe,
      setTransactionMessageFeePayer,
      setTransactionMessageLifetimeUsingBlockhash,
      signTransaction,
    } from "@solana/kit";
    import { getTransferSolInstruction } from "@solana-program/system"; // For adding custom instructions

    // ── Config ──────────────────────────────────────────────────────────────────
    const BASE_URL = "https://api.jup.ag/swap/v2";
    const COMPUTE_BUDGET_PROGRAM: Address = address(
      "ComputeBudget111111111111111111111111111111",
    );
    const COMPUTE_UNIT_LIMIT_MAX = 1_400_000;
    const CU_BUFFER_NUMERATOR = 120n;
    const CU_BUFFER_DENOMINATOR = 100n; // 120% of simulated CUs

    // ── Instruction types ───────────────────────────────────────────────────────

    type Account = {
      pubkey: Address;
      isSigner: boolean;
      isWritable: boolean;
    };

    type ApiInstruction = {
      programId: Address;
      accounts: Account[];
      data: Base64EncodedBytes;
    };

    function toAccountMeta(acc: Account): AccountMeta {
      const role =
        acc.isSigner && acc.isWritable
          ? AccountRole.WRITABLE_SIGNER
          : acc.isSigner
            ? AccountRole.READONLY_SIGNER
            : acc.isWritable
              ? AccountRole.WRITABLE
              : AccountRole.READONLY;
      return { address: acc.pubkey, role };
    }

    function createInstruction(
      ix: ApiInstruction,
    ): Instruction<string, readonly AccountMeta[]> {
      return {
        programAddress: ix.programId,
        accounts: ix.accounts.map(toAccountMeta),
        data: Uint8Array.from(getBase64Codec().encode(ix.data)),
      };
    }

    // ── Build response type ─────────────────────────────────────────────────────

    type BuildResponse = {
      inputMint: string;
      outputMint: string;
      inAmount: string;
      outAmount: string;
      otherAmountThreshold: string;
      swapMode: string;
      slippageBps: number;
      routePlan: {
        percent: number;
        bps: number;
        swapInfo: {
          ammKey: string;
          label: string;
          inputMint: string;
          outputMint: string;
          inAmount: string;
          outAmount: string;
        };
      }[];
      computeBudgetInstructions: ApiInstruction[];
      setupInstructions: ApiInstruction[];
      swapInstruction: ApiInstruction;
      cleanupInstruction: ApiInstruction | null;
      otherInstructions: ApiInstruction[];
      addressesByLookupTableAddress: Record<string, string[]> | null;
      blockhashWithMetadata: {
        blockhash: number[];
        lastValidBlockHeight: number;
      };
    };

    // ── Helpers ──────────────────────────────────────────────────────────────────

    function makeSetComputeUnitLimitIx(units: number): ApiInstruction {
      const data = Buffer.alloc(5);
      data.writeUInt8(0x02, 0);
      data.writeUInt32LE(units, 1);
      return {
        programId: COMPUTE_BUDGET_PROGRAM,
        accounts: [],
        data: data.toString("base64") as ApiInstruction["data"],
      };
    }

    function transformBlockhash(meta: BuildResponse["blockhashWithMetadata"]): {
      blockhash: Blockhash;
      lastValidBlockHeight: bigint;
    } {
      return {
        blockhash: getBase58Decoder().decode(
          Uint8Array.from(meta.blockhash),
        ) as Blockhash,
        lastValidBlockHeight: BigInt(meta.lastValidBlockHeight),
      };
    }

    function transformALTs(
      raw: Record<string, string[]> | null,
    ): AddressesByLookupTableAddress {
      if (!raw) return {};
      return Object.fromEntries(
        Object.entries(raw).map(([key, addrs]) => [
          address(key),
          addrs.map((a) => address(a)),
        ]),
      );
    }

    function buildTransaction(
      ixs: Instruction<string, readonly AccountMeta[]>[],
      blockhash: { blockhash: Blockhash; lastValidBlockHeight: bigint },
      alts: AddressesByLookupTableAddress,
      feePayer: Address,
    ) {
      return pipe(
        createTransactionMessage({ version: 0 }),
        (msg) => appendTransactionMessageInstructions(ixs, msg),
        (msg) => compressTransactionMessageUsingAddressLookupTables(msg, alts),
        (msg) => setTransactionMessageFeePayer(feePayer, msg),
        (msg) => setTransactionMessageLifetimeUsingBlockhash(blockhash, msg),
        (msg) => compileTransaction(msg),
      );
    }
    ```

    ```typescript @solana/web3.js expandable theme={null}
    import {
      ComputeBudgetProgram,
      Connection,
      Keypair,
      PublicKey,
      SystemProgram,
      TransactionInstruction,
      TransactionMessage,
      VersionedTransaction,
      AddressLookupTableAccount,
    } from "@solana/web3.js";
    import bs58 from "bs58";

    const COMPUTE_UNIT_LIMIT_MAX = 1_400_000;

    const BASE_URL = "https://api.jup.ag/swap/v2";

    // ── API response types ──────────────────────────────────────────────────────

    type ApiAccount = {
      pubkey: string;
      isSigner: boolean;
      isWritable: boolean;
    };

    type ApiInstruction = {
      programId: string;
      accounts: ApiAccount[];
      data: string; // base64
    };

    type BuildResponse = {
      inputMint: string;
      outputMint: string;
      inAmount: string;
      outAmount: string;
      otherAmountThreshold: string;
      swapMode: string;
      slippageBps: number;
      routePlan: {
        percent: number;
        bps: number;
        swapInfo: {
          ammKey: string;
          label: string;
          inputMint: string;
          outputMint: string;
          inAmount: string;
          outAmount: string;
        };
      }[];
      computeBudgetInstructions: ApiInstruction[];
      setupInstructions: ApiInstruction[];
      swapInstruction: ApiInstruction;
      cleanupInstruction: ApiInstruction | null;
      otherInstructions: ApiInstruction[];
      addressesByLookupTableAddress: Record<string, string[]> | null;
      blockhashWithMetadata: {
        blockhash: number[];
        lastValidBlockHeight: number;
      };
    };

    // ── Helpers ──────────────────────────────────────────────────────────────────

    function toInstruction(ix: ApiInstruction): TransactionInstruction {
      return new TransactionInstruction({
        programId: new PublicKey(ix.programId),
        keys: ix.accounts.map((acc) => ({
          pubkey: new PublicKey(acc.pubkey),
          isSigner: acc.isSigner,
          isWritable: acc.isWritable,
        })),
        data: Buffer.from(ix.data, "base64"),
      });
    }

    function transformALTs(
      raw: Record<string, string[]> | null,
    ): AddressLookupTableAccount[] {
      if (!raw) return [];
      return Object.entries(raw).map(
        ([key, addresses]) =>
          new AddressLookupTableAccount({
            key: new PublicKey(key),
            state: {
              deactivationSlot: BigInt("18446744073709551615"),
              lastExtendedSlot: 0,
              lastExtendedSlotStartIndex: 0,
              addresses: addresses.map((a) => new PublicKey(a)),
            },
          }),
      );
    }
    ```
  </CodeGroup>
</Accordion>

### Code example

<CodeGroup>
  ```typescript expandable title="@solana/kit" theme={null}
  const API_KEY = process.env.JUPITER_API_KEY;
  const RPC_URL = process.env.RPC_URL;
  if (!API_KEY) throw new Error("Missing JUPITER_API_KEY");
  if (!RPC_URL) throw new Error("Missing RPC_URL");

  const signer = await createKeyPairSignerFromBytes(
    getBase58Encoder().encode(process.env.BS58_PRIVATE_KEY!),
  );
  const rpc = createSolanaRpc(RPC_URL);

  // Step 1: Get swap instructions from /build
  const buildRes = await fetch(
    `${BASE_URL}/build?` +
      new URLSearchParams({
        inputMint: "So11111111111111111111111111111111111111112",
        outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        amount: "100000000",
        taker: signer.address,
        slippageBps: "100",
      }),
    { headers: { "x-api-key": API_KEY } },
  );
  if (!buildRes.ok) {
    console.error(`/build failed: ${buildRes.status}`, await buildRes.text());
    process.exit(1);
  }
  const build: BuildResponse = await buildRes.json();

  // Step 2: Collect instructions (no compute unit limit yet, we simulate first)
  // Example: add a SOL transfer after the swap
  const RECIPIENT = address("YOUR_RECIPIENT_ADDRESS");
  const transferIx = getTransferSolInstruction({
    source: signer,
    destination: RECIPIENT,
    amount: 1_000_000n, // 0.001 SOL
  });

  const instructions = [
    ...build.setupInstructions.map(createInstruction),
    createInstruction(build.swapInstruction),
    transferIx, // Your custom instruction, added after the swap
    ...(build.cleanupInstruction
      ? [createInstruction(build.cleanupInstruction)]
      : []),
    ...build.otherInstructions.map(createInstruction),
  ];

  // Step 3: Prepare blockhash and address lookup tables
  const blockhash = transformBlockhash(build.blockhashWithMetadata);
  const alts = transformALTs(build.addressesByLookupTableAddress);

  // Step 4: Simulate with max CU limit to estimate actual usage
  const simulationTx = buildTransaction(
    [
      createInstruction(makeSetComputeUnitLimitIx(COMPUTE_UNIT_LIMIT_MAX)),
      ...instructions,
    ],
    blockhash,
    alts,
    signer.address,
  );

  const simulationResult = await rpc
    .simulateTransaction(getBase64EncodedWireTransaction(simulationTx), {
      encoding: "base64",
      commitment: "confirmed",
      replaceRecentBlockhash: true,
    })
    .send();

  if (simulationResult.value.err) {
    console.error("Simulation failed:", simulationResult.value.err);
  }

  // Set 1.2x buffer on simulated CU, capped at max
  const estimatedCUL = simulationResult.value.unitsConsumed
    ? Math.min(
        Number(
          (simulationResult.value.unitsConsumed * CU_BUFFER_NUMERATOR) /
            CU_BUFFER_DENOMINATOR,
        ),
        COMPUTE_UNIT_LIMIT_MAX,
      )
    : COMPUTE_UNIT_LIMIT_MAX;
  console.log(
    `Simulated CUs: ${simulationResult.value.unitsConsumed}, using limit: ${estimatedCUL}`,
  );

  // Step 5: Build final transaction with estimated CU limit + CU price from response
  const compiledTx = buildTransaction(
    [
      createInstruction(makeSetComputeUnitLimitIx(estimatedCUL)),
      ...build.computeBudgetInstructions.map(createInstruction),
      ...instructions,
    ],
    blockhash,
    alts,
    signer.address,
  );

  // Step 6: Sign
  const signedTransaction = await signTransaction([signer.keyPair], compiledTx);

  // Step 7: Send via your own RPC
  const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
  const signature = await rpc
    .sendTransaction(wireTransaction, {
      encoding: "base64",
      skipPreflight: true,
    })
    .send();

  console.log(`Transaction sent: https://solscan.io/tx/${signature}`);
  ```

  ```typescript expandable title="@solana/web3.js" theme={null}
  const API_KEY = process.env.JUPITER_API_KEY;
  const RPC_URL = process.env.RPC_URL;
  if (!API_KEY) throw new Error("Missing JUPITER_API_KEY");
  if (!RPC_URL) throw new Error("Missing RPC_URL");

  const connection = new Connection(RPC_URL);
  const signer = Keypair.fromSecretKey(
    bs58.decode(process.env.BS58_PRIVATE_KEY!),
  );

  // Step 1: Get swap instructions from /build
  const buildRes = await fetch(
    `${BASE_URL}/build?` +
      new URLSearchParams({
        inputMint: "So11111111111111111111111111111111111111112",
        outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        amount: "100000000",
        taker: signer.publicKey.toString(),
        slippageBps: "100",
      }),
    { headers: { "x-api-key": API_KEY } },
  );
  if (!buildRes.ok) {
    console.error(`/build failed: ${buildRes.status}`, await buildRes.text());
    process.exit(1);
  }
  const build: BuildResponse = await buildRes.json();

  // Step 2: Collect instructions (no compute unit limit yet, we simulate first)
  // Example: add a SOL transfer after the swap
  const RECIPIENT = new PublicKey("YOUR_RECIPIENT_ADDRESS");
  const transferIx = SystemProgram.transfer({
    fromPubkey: signer.publicKey,
    toPubkey: RECIPIENT,
    lamports: 1_000_000, // 0.001 SOL
  });

  const instructions = [
    ...build.setupInstructions.map(toInstruction),
    toInstruction(build.swapInstruction),
    transferIx, // Your custom instruction, added after the swap
    ...(build.cleanupInstruction
      ? [toInstruction(build.cleanupInstruction)]
      : []),
    ...build.otherInstructions.map(toInstruction),
  ];

  // Step 3: Resolve address lookup tables (from /build response, no RPC needed)
  const addressLookupTableAccounts = transformALTs(
    build.addressesByLookupTableAddress,
  );

  const { blockhash, lastValidBlockHeight } = build.blockhashWithMetadata;
  const recentBlockhash = bs58.encode(Buffer.from(blockhash));

  // Step 4: Simulate with max CU limit to estimate actual usage
  const simulationMessage = new TransactionMessage({
    payerKey: signer.publicKey,
    recentBlockhash,
    instructions: [
      ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT_MAX }),
      ...instructions,
    ],
  }).compileToV0Message(addressLookupTableAccounts);

  const simulationTx = new VersionedTransaction(simulationMessage);
  const simulationResult = await connection.simulateTransaction(simulationTx, {
    replaceRecentBlockhash: true,
  });

  if (simulationResult.value.err) {
    console.error("Simulation failed:", simulationResult.value.err);
  }

  // Set 1.2x buffer on simulated CU, capped at max
  const estimatedCUL = simulationResult.value.unitsConsumed
    ? Math.min(
        Math.ceil(simulationResult.value.unitsConsumed * 1.2),
        COMPUTE_UNIT_LIMIT_MAX,
      )
    : COMPUTE_UNIT_LIMIT_MAX;
  console.log(
    `Simulated CUs: ${simulationResult.value.unitsConsumed}, using limit: ${estimatedCUL}`,
  );

  // Step 5: Build final transaction with estimated CU limit + CU price from response
  const finalMessage = new TransactionMessage({
    payerKey: signer.publicKey,
    recentBlockhash,
    instructions: [
      ComputeBudgetProgram.setComputeUnitLimit({ units: estimatedCUL }),
      ...build.computeBudgetInstructions.map(toInstruction), // CU price from response
      ...instructions,
    ],
  }).compileToV0Message(addressLookupTableAccounts);

  const transaction = new VersionedTransaction(finalMessage);
  transaction.sign([signer]);

  // Step 6: Send and confirm
  const signature = await connection.sendRawTransaction(transaction.serialize(), {
    skipPreflight: true,
  });

  console.log(`Transaction sent: https://solscan.io/tx/${signature}`);
  ```
</CodeGroup>

## How it works

### 1. Call `/build`

`GET /build` returns a quote and all the instructions you need to build a swap transaction.

**Required parameters:**

| Parameter    | Description                                    |
| ------------ | ---------------------------------------------- |
| `inputMint`  | Mint address of the token you are selling      |
| `outputMint` | Mint address of the token you are buying       |
| `amount`     | Amount in the smallest unit of the input token |
| `taker`      | Your wallet address                            |

**Key response fields:**

| Field                           | Description                                                          |
| ------------------------------- | -------------------------------------------------------------------- |
| `computeBudgetInstructions`     | Compute unit price instruction (does not include compute unit limit) |
| `setupInstructions`             | Pre-swap setup (e.g. ATA creation)                                   |
| `swapInstruction`               | The main swap instruction                                            |
| `cleanupInstruction`            | Post-swap cleanup (may be null)                                      |
| `otherInstructions`             | Additional instructions                                              |
| `addressesByLookupTableAddress` | Address lookup tables for v0 transactions                            |
| `blockhashWithMetadata`         | Recent blockhash and expiry height                                   |

Each instruction follows this structure:

```typescript  theme={null}
{
  programId: string,     // Program address
  accounts: [
    {
      pubkey: string,    // Account address
      isWritable: boolean,
      isSigner: boolean
    }
  ],
  data: string           // Base64-encoded instruction data
}
```

### 2. Add your own instructions

Insert custom instructions alongside the swap instructions. Common examples:

* SOL transfer (tip or payment)
* Memo instruction
* Create or close token accounts
* Custom program CPI

See [Other Instructions](/swap/build/other-instructions) for a reference of common instructions.

### 3. Simulate compute unit limit

The `/build` response includes `computeBudgetInstructions` with the compute unit price but **not** the compute unit limit. You need to simulate the transaction to determine the correct limit.

Why: integrators using `/build` typically add their own instructions, so the CU usage will differ from the base swap. The simulation gives you the actual CU consumed, and you set the limit to 1.2x that value (capped at 1,400,000).

Both code examples above demonstrate this: simulate with max CU limit, then rebuild with 1.2x the simulated value plus the CU price from the response. See [Solana fee structure](https://solana.com/docs/core/fees/fee-structure) for more on compute units and [Estimate Compute Units](/swap/advanced/compute-units) for a standalone guide.

### 4. Build a v0 transaction

The response includes address lookup tables in `addressesByLookupTableAddress`. Use these to compile a v0 (versioned) transaction, which supports more accounts than legacy transactions.

### 5. Sign and send

Sign the transaction with your wallet and send via your own RPC.

<Note>
  `/build` transactions cannot use `/execute`. You are responsible for sending the transaction via your own RPC and handling confirmation. Jupiter does not charge swap fees on `/build`.
</Note>

## Key differences from `/order`

|                         | `/order`                  | `/build`                                    |
| ----------------------- | ------------------------- | ------------------------------------------- |
| **Routing**             | All routers (Metis + RFQ) | Metis only                                  |
| **Swap fees**           | Jupiter platform fee      | None                                        |
| **Execution**           | Managed via `/execute`    | Self-managed                                |
| **Transaction control** | None                      | Full                                        |
| **Compute budget**      | Included in transaction   | Included as instructions (you can override) |

## Optional parameters

| Parameter                  | Default    | Description                                            |
| -------------------------- | ---------- | ------------------------------------------------------ |
| `slippageBps`              | 50         | Slippage tolerance in basis points                     |
| `mode`                     | (default)  | "fast" for reduced latency routing                     |
| `maxAccounts`              | 64         | Maximum accounts for the swap route (1-64)             |
| `platformFeeBps`           | 0          | Integrator platform fee in bps (requires `feeAccount`) |
| `feeAccount`               | -          | Token account to collect platform fees                 |
| `payer`                    | public key | Account that pays transaction fees and rent            |
| `wrapAndUnwrapSol`         | true       | Whether to wrap/unwrap SOL automatically               |
| `dexes`                    | (all)      | Restrict routing to specific DEXes                     |
| `excludeDexes`             | (none)     | Exclude specific DEXes from routing                    |
| `destinationTokenAccount`  | -          | SPL token account for output                           |
| `nativeDestinationAccount` | -          | Native SOL account for output                          |
| `blockhashSlotsToExpiry`   | 150        | Slots until blockhash expires (1-300)                  |

For the full parameter reference, see the [API reference](/api-reference/swap). For how parameters affect routing, see [Routing](/swap/routing).

## Related

* [Other Instructions](/swap/build/other-instructions) for common instructions to compose with your swap
* [Routing](/swap/routing) for how parameters affect routing
* [Fees](/swap/fees) for adding platform fees to `/build`
* [Advanced Techniques](/swap/advanced) for CU simulation, `mode: "fast"`, and `maxAccounts`
* [API Reference: GET /build](/api-reference/swap/build) for the full OpenAPI specification


Built with [Mintlify](https://mintlify.com).