> ## 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.

# Build

> Raw swap instructions with full transaction control via Metis onchain routing

The Router path uses the `/build` endpoint to return raw swap instructions instead of an assembled transaction. Routing is handled by Metis, Jupiter's onchain routing engine, which finds the best path across Solana DEXes.

You build the transaction yourself, giving you full control to add custom instructions, CPI, or modify any part of the transaction. Once built and signed, submit via your own RPC or use [`/submit`](/transaction/submit) to land through Jupiter's transaction infrastructure with SOL tips.

## 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 COMPUTE_BUDGET_PROGRAM: Address = address(
      "ComputeBudget111111111111111111111111111111",
    );
    const COMPUTE_UNIT_LIMIT_MAX = 1_400_000;

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

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

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

    function createInstruction(ix: ApiInstruction): Instruction {
      return {
        programAddress: ix.programId,
        accounts: ix.accounts.map((acc) => ({
          address: acc.pubkey,
          role: acc.isSigner && acc.isWritable
            ? AccountRole.WRITABLE_SIGNER
            : acc.isSigner
              ? AccountRole.READONLY_SIGNER
              : acc.isWritable
                ? AccountRole.WRITABLE
                : AccountRole.READONLY,
        })),
        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[];
      tipInstruction: ApiInstruction | null;
      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[],
      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;

    // ── 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[];
      tipInstruction: ApiInstruction | null;
      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(
    "https://api.jup.ag/swap/v2/build?" +
      new URLSearchParams({
        inputMint: "So11111111111111111111111111111111111111112",
        outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        amount: "100000000",
        taker: signer.address,
        slippageBps: "100",
        tipAmount: "1000000", // 0.001 SOL tip for /submit (omit if using your own RPC)
      }),
    { 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),
    ...(build.tipInstruction
      ? [createInstruction(build.tipInstruction)]
      : []),
  ];

  // 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(
        Math.ceil(Number(simulationResult.value.unitsConsumed) * 1.2),
        COMPUTE_UNIT_LIMIT_MAX,
      )
    : COMPUTE_UNIT_LIMIT_MAX;

  // 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 and submit via /submit (or use your own RPC / transaction pipeline)
  const signedTransaction = await signTransaction([signer.keyPair], compiledTx);

  const submitRes = await fetch("https://api.jup.ag/tx/v1/submit", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": API_KEY,
    },
    body: JSON.stringify({
      signedTransaction: getBase64EncodedWireTransaction(signedTransaction),
    }),
  });

  const { signature } = await submitRes.json();
  console.log("Submitted:", `https://solscan.io/tx/${signature}`);

  // Step 7: Confirm the transaction landed
  const confirmation = await rpc
    .confirmTransaction(signature, {
      strategy: { type: "blockhash", ...blockhash },
      commitment: "confirmed",
    })
    .send();

  if (confirmation.value.err) {
    console.error("Transaction failed:", confirmation.value.err);
    process.exit(1);
  }

  console.log("Confirmed:", 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(
    "https://api.jup.ag/swap/v2/build?" +
      new URLSearchParams({
        inputMint: "So11111111111111111111111111111111111111112",
        outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        amount: "100000000",
        taker: signer.publicKey.toString(),
        slippageBps: "100",
        tipAmount: "1000000", // 0.001 SOL tip for /submit (omit if using your own RPC)
      }),
    { 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),
    ...(build.tipInstruction
      ? [toInstruction(build.tipInstruction)]
      : []),
  ];

  // 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;

  // 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);

  // Step 6: Sign and submit via /submit (or use your own RPC / transaction pipeline)
  const transaction = new VersionedTransaction(finalMessage);
  transaction.sign([signer]);

  const submitRes = await fetch("https://api.jup.ag/tx/v1/submit", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": API_KEY,
    },
    body: JSON.stringify({
      signedTransaction: Buffer.from(transaction.serialize()).toString("base64"),
    }),
  });

  const { signature } = await submitRes.json();
  console.log("Submitted:", `https://solscan.io/tx/${signature}`);

  // Step 7: Confirm the transaction landed
  const confirmation = await connection.confirmTransaction(
    { signature, blockhash: recentBlockhash, lastValidBlockHeight },
    "confirmed",
  );

  if (confirmation.value.err) {
    console.error("Transaction failed:", confirmation.value.err);
    process.exit(1);
  }

  console.log("Confirmed:", 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                                              |
| `tipInstruction`                | SOL tip transfer instruction (present when `tipAmount` is provided)  |
| `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 [Common Instructions](/swap/build/common-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, or use [`/submit`](/transaction/submit) to land them through Jupiter's transaction infrastructure with SOL tips.

<Note>
  `/build` transactions cannot use `/execute`.

  * `/build` does not have the required `requestId`
  * `/build` is intended for customisations, and `/execute` validates the transaction to prevent any changes
</Note>

## Router vs Meta-Aggregator

|                         | Meta-Aggregator (`/order`)                | Router (`/build`)                                                                                           |
| ----------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Routing**             | All engines (Metis, JupiterZ, Dflow, OKX) | Metis only                                                                                                  |
| **Swap fees**           | Jupiter platform fee                      | None                                                                                                        |
| **Transaction landing** | Managed via `/execute`                    | Self-managed or use Jupiter's proprietary transaction landing pipeline via [`/submit`](/transaction/submit) |
| **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, or `"rtse"` for [RTSE](/swap/advanced/slippage)                                                                                                                          |
| `tipAmount`                  | -          | SOL tip amount in lamports. Adds a tip instruction for use with [`/submit`](/transaction/submit)                                                                                                             |
| `computeUnitPricePercentile` | -          | Priority fee percentile for the CU price instruction. Named levels: `"medium"` (25th), `"high"` (50th), `"veryHigh"` (75th), or an integer 0-10000 in bps. See [Compute Units](/swap/advanced/compute-units) |
| `mode`                       | (default)  | "fast" for reduced latency routing (BETA)                                                                                                                                                                    |
| `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).

## Fees

Jupiter does not charge swap fees on `/build`. The only fee mechanism is integrator platform fees via `platformFeeBps` and `feeAccount`:

```typescript theme={null}
const params = new URLSearchParams({
  inputMint: "So11111111111111111111111111111111111111112",
  outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  amount: "1000000000",
  taker: walletAddress,
  platformFeeBps: "50",          // 0.5%
  feeAccount: "YOUR_FEE_TOKEN_ACCOUNT",
});

const response = await fetch(
  `https://api.jup.ag/swap/v2/build?${params}`,
  { headers: { "x-api-key": API_KEY } }
);
```

Your fee is added as part of the swap instruction. The `feeAccount` can be any SPL token account you control (it does not need to be a referral program account). You are responsible for creating and managing this account.

## Related

* [Transaction Submission](/transaction/submit) to submit via Jupiter's transaction landing infrastructure with SOL tips for priority processing
* [Common Instructions](/swap/build/common-instructions) for common instructions to compose with your swap
* [Advanced Techniques](/swap/advanced) for CU simulation, `mode: "fast"`, and `maxAccounts`
* [API Reference: GET /build](/api-reference/swap/build) for the full OpenAPI specification
