From 800e942e5e7b4dd9757bae6094ed33f3bac4f050 Mon Sep 17 00:00:00 2001 From: alchemy-bot <80712764+alchemy-bot@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:00:14 +0000 Subject: [PATCH 1/5] [docs-agent] Add Solana Trader API docs (REST spec, overview, nav) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new Solana Trader API surface under Chains > Solana with three operations: * GET /order — swap quote plus optional assembled base64 transaction when a taker is supplied. * GET /build — raw swap instructions for composing into your own transaction. * POST /quote-multiple-output-mints — quote a single input mint against up to 32 candidate output mints in one call. Spec: src/openapi/solana-trader/solana-trader.yaml, OpenAPI 3.1 modeled on src/openapi/prices/prices.yaml. Server https://solana-mainnet.g.alchemy.com/v2 with {apiKey} in the path (docs-demo default). Introduces x-compute-units and x-rate-limit-cus on each operation (100 placeholders), the first REST spec in this repo to carry those extensions. Overview: content/api-reference/solana/solana-trader-api-overview.mdx patterned after solana-jito-bundles-overview.mdx (not the historical Photon nft-api location). Nav slot in content/docs.yml placed between Solana Photon API and Solana Jito Bundles and Transactions, with flattened: true so method pages nest under the overview. Also adds a # Solana: Trader API block in content/api-reference/pricing-resources/pricing/compute-unit-costs.mdx between the Photon and Jito blocks (100 CU / 100 throughput placeholders), and a Related APIs entry on content/api-reference/solana/solana-api-overview.mdx. Refs DOCS-227 --- .../pricing/compute-unit-costs.mdx | 12 + .../solana/solana-api-overview.mdx | 1 + .../solana/solana-trader-api-overview.mdx | 47 ++ content/docs.yml | 7 + src/openapi/solana-trader/solana-trader.yaml | 584 ++++++++++++++++++ 5 files changed, 651 insertions(+) create mode 100644 content/api-reference/solana/solana-trader-api-overview.mdx create mode 100644 src/openapi/solana-trader/solana-trader.yaml diff --git a/content/api-reference/pricing-resources/pricing/compute-unit-costs.mdx b/content/api-reference/pricing-resources/pricing/compute-unit-costs.mdx index b84d94385..d2808a32b 100644 --- a/content/api-reference/pricing-resources/pricing/compute-unit-costs.mdx +++ b/content/api-reference/pricing-resources/pricing/compute-unit-costs.mdx @@ -226,6 +226,18 @@ For more details, check out the [Compute Units](/docs/reference/compute-units#wh | getValidityProofV2 | 1200 | 500 | {/* cu:auto end */} +# Solana: Trader API + +[Solana Trader API](/docs/chains/solana/solana-trader-api) endpoints return swap quotes and, on request, an assembled Solana transaction. Alchemy currently routes Trader API requests through Metis. + +{/* cu:auto product="solana-trader" */} +| Method | CU | Throughput CU | +| --------------------------- | --- | ------------- | +| build | 100 | 100 | +| order | 100 | 100 | +| quote-multiple-output-mints | 100 | 100 | +{/* cu:auto end */} + # Solana: Jito Bundles and Transactions [Jito bundles](/docs/chains/solana/solana-jito-bundles-and-transactions) are groups of up to 5 signed transactions that execute sequentially and atomically in a single slot on Solana Mainnet. A tip to one of the Jito tip accounts is required for inclusion. These methods are available on paid Alchemy plans only. diff --git a/content/api-reference/solana/solana-api-overview.mdx b/content/api-reference/solana/solana-api-overview.mdx index 76b6c1675..4b650ddda 100644 --- a/content/api-reference/solana/solana-api-overview.mdx +++ b/content/api-reference/solana/solana-api-overview.mdx @@ -48,3 +48,4 @@ The following Alchemy APIs are also supported on Solana: * [Bundler API](/docs/wallets/transactions/low-level-infra/bundler/overview) * [Gas Manager API](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) +* [Solana Trader API](/docs/chains/solana/solana-trader-api) — REST endpoints for pricing and building Solana token swaps. diff --git a/content/api-reference/solana/solana-trader-api-overview.mdx b/content/api-reference/solana/solana-trader-api-overview.mdx new file mode 100644 index 000000000..70f8001a8 --- /dev/null +++ b/content/api-reference/solana/solana-trader-api-overview.mdx @@ -0,0 +1,47 @@ +--- +title: Solana Trader API +description: Alchemy's REST endpoints for pricing and building Solana token swaps. +subtitle: Alchemy's REST endpoints for pricing and building Solana token swaps. +--- + +## Background + +The Solana Trader API returns swap quotes and, on request, an assembled +transaction that a taker can sign and broadcast. Use it to price a swap ahead +of time, present a route to a user, or hand a signable transaction to a +wallet. Requests use the same Alchemy API key as your other Solana calls. + +Alchemy routes Trader API requests through [Metis](https://station.jup.ag/blog/jupiter-swap-v2-metis), the routing engine that powers Jupiter's Swap V2 `/order` endpoint. If you have already integrated Jupiter's contract, the response shape here will feel familiar. Two differences worth calling out: + +* Alchemy carries auth in the URL path (see [Endpoint](#endpoint)), not in an `x-api-key` header. +* `/quote-multiple-output-mints` is an Alchemy-specific endpoint that returns quotes for one input mint against up to 32 candidate output mints in a single request. + +The API exposes three endpoints: + +* `GET /order` returns a swap quote. Pass a `taker` to also receive an unsigned base64 transaction; omit `taker` for a quote-only response. +* `GET /build` returns raw swap instructions for composing into your own transaction (multi-leg flows, program deposits, cleanup steps). +* `POST /quote-multiple-output-mints` returns quotes for a single input against up to 32 output mints in one call. + +## Endpoint + +Solana Trader API is available on **Solana Mainnet** through the standard Alchemy Solana endpoint. Devnet is not supported. + +```text +https://solana-mainnet.g.alchemy.com/v2/{apiKey} +``` + +Use your Solana Mainnet API key from the [Alchemy Dashboard](https://dashboard.alchemy.com/apps). The `docs-demo` key powers the Try It widget on the method pages below. + +## Methods + +| Method | Function | CU cost | Throughput CUs (how many CUs this will count for towards your CUs per second capacity) | +| --- | --- | --- | --- | +| [GET /order](/docs/chains/solana/solana-trader-api/solana-trader-api/get-swap-order) | Get a swap quote. Include a `taker` to also receive an unsigned base64 transaction; omit `taker` for quote fields only. | 100 | 100 | +| [GET /build](/docs/chains/solana/solana-trader-api/solana-trader-api/build-swap-instructions) | Return raw swap instructions for composing into your own transaction. | 100 | 100 | +| [POST /quote-multiple-output-mints](/docs/chains/solana/solana-trader-api/solana-trader-api/quote-multiple-output-mints) | Quote a single input mint against up to 32 candidate output mints in one call. | 100 | 100 | + +## Related + +* [`sendTransaction`](/docs/chains/solana/solana-api-endpoints/send-transaction) — submit the signed transaction returned by `/order` to the cluster. +* [Solana Jito Bundles and Transactions](/docs/chains/solana/solana-jito-bundles-and-transactions) — pair a signed swap with a Jito tip for atomic, low-latency inclusion. +* [Solana API Overview](/docs/solana/solana-api-overview) — the full Solana JSON-RPC surface. diff --git a/content/docs.yml b/content/docs.yml index 04a0e577c..41f71336a 100644 --- a/content/docs.yml +++ b/content/docs.yml @@ -365,6 +365,13 @@ navigation: - api: Solana Photon API api-name: solana-photon flattened: true + - section: Solana Trader API + path: >- + api-reference/solana/solana-trader-api-overview.mdx + contents: + - api: Solana Trader API + api-name: solana-trader + flattened: true - section: Solana Jito Bundles and Transactions path: >- api-reference/solana/solana-jito-bundles-overview.mdx diff --git a/src/openapi/solana-trader/solana-trader.yaml b/src/openapi/solana-trader/solana-trader.yaml new file mode 100644 index 000000000..679a12856 --- /dev/null +++ b/src/openapi/solana-trader/solana-trader.yaml @@ -0,0 +1,584 @@ +# yaml-language-server: $schema=https://spec.openapis.org/oas/3.1/schema/2022-10-07 + +openapi: 3.1.0 +info: + title: 💱 Solana Trader API + description: > + Alchemy's Solana Trader API returns swap quotes and, on request, an assembled + signable transaction for routing token swaps on Solana. Requests are + authenticated with your Alchemy API key in the URL path, the same key that + powers your other Solana RPC calls. Alchemy currently routes Trader API + requests through Metis. + version: "1.0" +servers: + - url: https://solana-mainnet.g.alchemy.com/v2 + description: Solana Mainnet +paths: + "/{apiKey}/order": + get: + summary: Get Order + description: > + Returns a swap quote for `inputMint` → `outputMint`. When `taker` is + supplied, the response also includes an unsigned, base64-encoded Solana + transaction the taker can sign and broadcast. When `taker` is omitted, + the response contains the quote only and `transaction` is `null`. + tags: ["Solana Trader API Endpoints"] + x-compute-units: 100 + x-rate-limit-cus: 100 + operationId: get-swap-order + parameters: + - $ref: "#/components/parameters/apiKey" + - $ref: "#/components/parameters/InputMint" + - $ref: "#/components/parameters/OutputMint" + - $ref: "#/components/parameters/Amount" + - name: taker + in: query + required: false + description: > + Public key of the wallet that will sign the transaction. When + omitted, the response returns a quote and `transaction` is `null`. + schema: + type: string + example: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + - name: receiver + in: query + required: false + description: > + Public key of the wallet that will receive the output tokens. Must + differ from `taker`. Expects a wallet address, not a token account. + For non-SOL output, tokens are sent to the receiver's associated + token account (ATA); a create-ATA instruction is added when the ATA + does not exist. + schema: + type: string + - name: swapMode + in: query + required: false + description: Swap mode. Currently only `ExactIn` is supported. + schema: + type: string + enum: [ExactIn] + default: ExactIn + - name: slippageBps + in: query + required: false + description: > + Slippage tolerance in basis points (0-10000). Alchemy picks a value + automatically when omitted. + schema: + type: integer + minimum: 0 + maximum: 10000 + example: 50 + - name: priorityFeeLamports + in: query + required: false + description: > + Priority fee in lamports. Overrides the automatic priority fee + Alchemy would otherwise pick. + schema: + type: integer + - name: jitoTipLamports + in: query + required: false + description: Jito MEV tip in lamports for faster block inclusion. + schema: + type: integer + - name: broadcastFeeType + in: query + required: false + description: > + Fee cap strategy. `maxCap` treats the supplied fee as a maximum; + `exactFee` sends the exact amount. Ignored when neither + `priorityFeeLamports` nor `jitoTipLamports` is set. + schema: + type: string + enum: [maxCap, exactFee] + - name: excludeDexes + in: query + required: false + description: > + Comma-separated list of DEX labels to exclude from routing. Labels + are case-sensitive (e.g. `Raydium,Orca+V2,Meteora+DLMM`). + schema: + type: string + responses: + "200": + description: Quote with optional assembled transaction. + content: + application/json: + schema: + $ref: "#/components/schemas/OrderResponse" + "400": + description: Malformed request or invalid parameters. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: Missing or invalid API key. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: Rate limit exceeded. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + + "/{apiKey}/build": + get: + summary: Build Swap Instructions + description: > + Returns raw Solana swap instructions rather than a pre-built transaction. + Use `/build` when you need to compose the swap into a larger transaction + of your own (for example a program deposit before the swap or a + cleanup step after it). If you just need a signable transaction, use + `/order` with a `taker`. + tags: ["Solana Trader API Endpoints"] + x-compute-units: 100 + x-rate-limit-cus: 100 + operationId: build-swap-instructions + parameters: + - $ref: "#/components/parameters/apiKey" + - $ref: "#/components/parameters/InputMint" + - $ref: "#/components/parameters/OutputMint" + - $ref: "#/components/parameters/Amount" + - name: taker + in: query + required: true + description: > + Public key of the wallet that will sign the swap instructions. + Required for `/build` since the instructions must reference concrete + source and destination accounts. + schema: + type: string + example: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + - name: receiver + in: query + required: false + description: Public key of the wallet that will receive the output tokens. + schema: + type: string + - name: swapMode + in: query + required: false + description: Swap mode. Currently only `ExactIn` is supported. + schema: + type: string + enum: [ExactIn] + default: ExactIn + - name: slippageBps + in: query + required: false + description: > + Slippage tolerance in basis points (0-10000). Alchemy picks a value + automatically when omitted. + schema: + type: integer + minimum: 0 + maximum: 10000 + example: 50 + - name: excludeDexes + in: query + required: false + description: Comma-separated list of DEX labels to exclude from routing. + schema: + type: string + responses: + "200": + description: Raw swap instructions. + content: + application/json: + schema: + $ref: "#/components/schemas/BuildResponse" + "400": + description: Malformed request or invalid parameters. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: Missing or invalid API key. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: Rate limit exceeded. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + + "/{apiKey}/quote-multiple-output-mints": + post: + summary: Quote Multiple Output Mints + description: > + Returns quotes for a single input mint against up to 32 candidate output + mints in one call. Useful for pricing a basket or comparing routes + across candidate outputs without issuing N separate requests. Each + entry in `quotes` follows the same shape as `/order` without a `taker`, + so responses carry the quote fields only and `transaction` is `null`. + tags: ["Solana Trader API Endpoints"] + x-compute-units: 100 + x-rate-limit-cus: 100 + operationId: quote-multiple-output-mints + parameters: + - $ref: "#/components/parameters/apiKey" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MultiQuoteRequest" + responses: + "200": + description: Array of quotes, one per requested output mint. + content: + application/json: + schema: + $ref: "#/components/schemas/MultiQuoteResponse" + "400": + description: > + Malformed request or invalid parameters. Common cause is submitting + more than 32 output mints in `outputMints`. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "401": + description: Missing or invalid API key. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: Rate limit exceeded. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + +components: + securitySchemes: + apiKey: + type: apiKey + name: Authorization + in: header + description: > + Trader API requests carry the API key in the URL path, not in a header. + This scheme is defined to satisfy tooling and is unused by the + endpoints. + x-default: Bearer API_KEY + parameters: + apiKey: + name: apiKey + in: path + required: true + schema: + type: string + default: docs-demo + description: For higher throughput, [create your own API key](https://dashboard.alchemy.com/signup). + InputMint: + name: inputMint + in: query + required: true + description: Mint address of the input token. + schema: + type: string + default: So11111111111111111111111111111111111111112 + example: So11111111111111111111111111111111111111112 + OutputMint: + name: outputMint + in: query + required: true + description: Mint address of the output token. + schema: + type: string + default: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + example: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + Amount: + name: amount + in: query + required: true + description: > + Amount to swap in the smallest unit of the input token (lamports for + native SOL, base units for SPL tokens). + schema: + type: string + default: "1000000000" + example: "1000000000" + schemas: + OrderResponse: + type: object + description: Quote fields, plus an assembled transaction when a taker was supplied. + properties: + inputMint: + type: string + description: Mint of the input token, echoed back. + outputMint: + type: string + description: Mint of the output token, echoed back. + inAmount: + type: string + description: Input amount used for the quote, in base units of the input mint. + outAmount: + type: string + description: Estimated output amount before slippage, in base units of the output mint. + otherAmountThreshold: + type: string + description: Minimum output amount after slippage is applied. + swapMode: + type: string + description: Swap mode used to build the quote. + enum: [ExactIn] + slippageBps: + type: integer + description: Slippage tolerance in basis points. + priceImpact: + type: number + description: > + Price impact of the swap, in percentage points (for example, `-0.1` + means -0.1%). Divide by 100 to convert to a decimal fraction. + routePlan: + type: array + description: Ordered list of route steps that make up the swap. + items: + $ref: "#/components/schemas/RoutePlanStep" + router: + type: string + description: > + Router that produced the quote. Alchemy currently serves quotes + through Metis only. + enum: [metis] + transaction: + type: ["string", "null"] + description: > + Base64-encoded, unsigned Solana transaction. `null` when `taker` + was not supplied. Empty string when `taker` was supplied but the + router could not build a transaction — inspect `errorCode` and + `errorMessage` in that case. + lastValidBlockHeight: + type: string + description: Block height beyond which the assembled transaction is no longer valid. + signatureFeeLamports: + type: integer + description: Estimated signature fee in lamports. + prioritizationFeeLamports: + type: integer + description: Estimated priority fee plus any Jito tip, in lamports. + rentFeeLamports: + type: integer + description: > + Estimated rent fee in lamports (for example, to open the output + token account when it does not already exist). + requestId: + type: string + description: Unique identifier for the request. Include when reporting issues. + taker: + type: ["string", "null"] + description: Taker echoed back from the request. `null` when not supplied. + errorCode: + type: integer + description: > + Present only when `taker` was supplied and `transaction` is the + empty string. Match on `errorCode` to identify the failure: + + * `1` — insufficient funds + * `2` — insufficient SOL for gas + * `3` — swap below minimum for gasless + errorMessage: + type: string + description: > + Human-readable error description. Present when `errorCode` is set. + Match on `errorCode` rather than parsing this string. + required: + - inputMint + - outputMint + - inAmount + - outAmount + - swapMode + - slippageBps + - routePlan + - router + + BuildResponse: + type: object + description: Raw Solana swap instructions. + properties: + setupInstructions: + type: array + description: > + Instructions that must run before the swap (for example, create an + associated token account or wrap SOL). + items: + $ref: "#/components/schemas/Instruction" + swapInstruction: + $ref: "#/components/schemas/Instruction" + cleanupInstruction: + description: > + Optional instruction that runs after the swap (for example, close a + temporary wrapped-SOL account and reclaim rent). `null` when no + cleanup is required. + oneOf: + - $ref: "#/components/schemas/Instruction" + - type: "null" + addressLookupTableAddresses: + type: array + description: Address lookup table accounts the swap references. + items: + type: string + computeUnitLimit: + type: integer + description: Recommended compute unit limit for the transaction. + prioritizationFeeLamports: + type: integer + description: Recommended prioritization fee in lamports. + requestId: + type: string + description: Unique identifier for the request. + required: + - swapInstruction + + Instruction: + type: object + description: A Solana instruction in decomposed form. + properties: + programId: + type: string + description: Program ID that owns the instruction. + accounts: + type: array + items: + $ref: "#/components/schemas/AccountMeta" + data: + type: string + description: Base64-encoded instruction data. + required: + - programId + - accounts + - data + + AccountMeta: + type: object + properties: + pubkey: + type: string + isSigner: + type: boolean + isWritable: + type: boolean + required: + - pubkey + - isSigner + - isWritable + + RoutePlanStep: + type: object + properties: + swapInfo: + $ref: "#/components/schemas/SwapInfo" + percent: + type: number + description: Percentage of the total swap routed through this step (0-100). + required: + - swapInfo + - percent + + SwapInfo: + type: object + properties: + ammKey: + type: string + description: On-chain address of the AMM pool used for this step. + label: + type: string + description: Human-readable DEX label (for example, `Orca V2`, `Raydium CLMM`). + inputMint: + type: string + outputMint: + type: string + inAmount: + type: string + outAmount: + type: string + required: + - ammKey + - label + - inputMint + - outputMint + - inAmount + - outAmount + + MultiQuoteRequest: + type: object + properties: + inputMint: + type: string + description: Mint address of the input token. + example: So11111111111111111111111111111111111111112 + default: So11111111111111111111111111111111111111112 + amount: + type: string + description: Amount to swap, in base units of the input mint. + example: "1000000000" + default: "1000000000" + outputMints: + type: array + description: > + Mint addresses of the candidate output tokens. Up to 32 entries per + request. + minItems: 1 + maxItems: 32 + items: + type: string + example: + - EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + - Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB + swapMode: + type: string + enum: [ExactIn] + default: ExactIn + slippageBps: + type: integer + minimum: 0 + maximum: 10000 + description: Slippage tolerance in basis points applied to each quote. + required: + - inputMint + - amount + - outputMints + + MultiQuoteResponse: + type: object + properties: + quotes: + type: array + description: > + One quote per requested output mint, in the same order as + `outputMints` in the request. + items: + $ref: "#/components/schemas/OrderResponse" + requestId: + type: string + description: Unique identifier for the request. + required: + - quotes + + ErrorResponse: + type: object + properties: + error: + type: string + description: Human-readable error description. + errorCode: + type: integer + description: Machine-readable error code, when available. + requestId: + type: string + description: Unique identifier for the request. + required: + - error From 337c818f39055d2256a6ade4e3469b8610112887 Mon Sep 17 00:00:00 2001 From: alchemy-bot <80712764+alchemy-bot@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:09:06 +0000 Subject: [PATCH 2/5] [docs-agent] Flatten Solana Trader API sidebar (drop operation tags) Vlad flagged that the sidebar was rendering as Solana Trader API > Solana Trader API Endpoints > , one level deeper than Solana Photon API. The extra middle level was the OpenAPI operation tag ('Solana Trader API Endpoints'): Fern groups OpenAPI operations by tag under the api entry even with flattened: true, whereas Solana Photon's OpenRPC methods carry no tags and render flat. Fix matches the Portfolio spec pattern (src/openapi/portfolio/portfolio.yaml uses no tags on its operations and renders flat under a single api heading in docs.yml). Drops tags from /order, /build, and /quote-multiple-output-mints so all three operations render directly under the Solana Trader API section. Refs DOCS-227 --- src/openapi/solana-trader/solana-trader.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/openapi/solana-trader/solana-trader.yaml b/src/openapi/solana-trader/solana-trader.yaml index 679a12856..624f99b06 100644 --- a/src/openapi/solana-trader/solana-trader.yaml +++ b/src/openapi/solana-trader/solana-trader.yaml @@ -22,7 +22,6 @@ paths: supplied, the response also includes an unsigned, base64-encoded Solana transaction the taker can sign and broadcast. When `taker` is omitted, the response contains the quote only and `transaction` is `null`. - tags: ["Solana Trader API Endpoints"] x-compute-units: 100 x-rate-limit-cus: 100 operationId: get-swap-order @@ -137,7 +136,6 @@ paths: of your own (for example a program deposit before the swap or a cleanup step after it). If you just need a signable transaction, use `/order` with a `taker`. - tags: ["Solana Trader API Endpoints"] x-compute-units: 100 x-rate-limit-cus: 100 operationId: build-swap-instructions @@ -222,7 +220,6 @@ paths: across candidate outputs without issuing N separate requests. Each entry in `quotes` follows the same shape as `/order` without a `taker`, so responses carry the quote fields only and `transaction` is `null`. - tags: ["Solana Trader API Endpoints"] x-compute-units: 100 x-rate-limit-cus: 100 operationId: quote-multiple-output-mints From fab6e2502e6f2a7fe3588d561d3658a20324bec0 Mon Sep 17 00:00:00 2001 From: alchemy-bot <80712764+alchemy-bot@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:40:25 +0000 Subject: [PATCH 3/5] [docs-agent] Solana Trader API spec review pass (vlad's nits) Comprehensive rewrite of src/openapi/solana-trader/solana-trader.yaml against Vlad's spec-review pass. /order: * swapMode enum extended with ExactOut. otherAmountThreshold documented as max input on ExactOut, min output on ExactIn. * Response fields promoted to required (always present): mode, otherAmountThreshold, priceImpact, priceImpactPct, inUsdValue, outUsdValue, swapUsdValue, swapType, gasless, router, routePlan, transaction, taker, signatureFeeLamports, prioritizationFeeLamports, rentFeeLamports, signatureFeePayer, prioritizationFeePayer, rentFeePayer. transaction/taker/fee-payer fields nullable via type: [string, null]. * lastValidBlockHeight kept optional (omitted, not null, when there is no usable transaction). * Router-side requestId dropped (not emitted by this service; out of scope with /execute per Metis's README). * errorCode 3 dropped (never emitted, no gasless path). errorCode 1 and 2 retained. Added error field alongside errorCode/errorMessage for 200 rejections. * routePlan[] steps gained bps (0-10000) and optional usdValue. percent typed as integer 0-100. * Error responses (400, 401, 429) removed per Vlad's ask (200 only for now). * Three named examples: quoteOnly, withTransaction, pricedButUnexecutable. /build: * Request params overhauled. Dropped receiver and swapMode (silently ignored by Metis). slippageBps clarified: Metis default 50 when omitted, string rtse accepted alongside 0-10000 integer. * Added payer, dexes (mutually exclusive with excludeDexes), platformFeeBps (0-10000 with feeAccount required when positive), feeAccount, maxAccounts (1-64), wrapAndUnwrapSol, destinationTokenAccount / nativeDestinationAccount (mutually exclusive), blockhashSlotsToExpiry (1-300, with 256-300 as 501 note), tipAmount (> 0), computeUnitPricePercentile (medium | high | veryHigh only), mode (fast only), forJitoBundle. * Response schema rewritten. Dropped addressLookupTableAddresses, computeUnitLimit, prioritizationFeeLamports, requestId. * Added quote fields (inputMint, outputMint, inAmount, outAmount, otherAmountThreshold, swapMode ExactIn-only, slippageBps, priceImpactPct, routePlan), computeBudgetInstructions, otherInstructions, tipInstruction, addressesByLookupTableAddress (map keyed by lookup-table address, null when no tables in use), blockhashWithMetadata with a snake_case fetchedAt object (secs_since_epoch, nanos_since_epoch) and 32-byte integer array blockhash. * setupInstructions kept required (always present, may be empty). * Errors documented: 400 with plain {error: string} body, 501 for numeric computeUnitPricePercentile and blockhashSlotsToExpiry 256-300, 502 for Metis / priority-fee / lookup-table / blockhash / tip-instruction upstream failures. 401 and 429 omitted (gateway-only). * Single realistic 200 example. /quote-multiple-output-mints: * Response envelope rewritten. quotes changed from array to object keyed by output mint (in request order), each value only { inAmount, outAmount }. Shared contextSlot at the top level (omitted when no quote carried one). requestId removed. * Request body: dropped slippageBps (silently ignored server-side). Kept inputMint, outputMints (1-32 unique pubkeys, none equal to inputMint), amount (JSON string; number returns 400), swapMode (ExactIn or ExactOut, ExactOut = desired output per mint independently). * 400 documented with common causes (over 32 mints, amount as number, duplicate mint, mint equal to input). * Introduced dedicated MultiQuoteEntry component instead of reusing OrderResponse. Shared: * ErrorResponse simplified to { error: string } only. * Amount parameter description updated for ExactOut semantics. * redocly source lint clean (2 baseline warnings: info-license and operation-4xx-response on /order, both intentional). Refs DOCS-227 --- src/openapi/solana-trader/solana-trader.yaml | 810 +++++++++++++++---- 1 file changed, 671 insertions(+), 139 deletions(-) diff --git a/src/openapi/solana-trader/solana-trader.yaml b/src/openapi/solana-trader/solana-trader.yaml index 624f99b06..06ddcbe26 100644 --- a/src/openapi/solana-trader/solana-trader.yaml +++ b/src/openapi/solana-trader/solana-trader.yaml @@ -21,7 +21,7 @@ paths: Returns a swap quote for `inputMint` → `outputMint`. When `taker` is supplied, the response also includes an unsigned, base64-encoded Solana transaction the taker can sign and broadcast. When `taker` is omitted, - the response contains the quote only and `transaction` is `null`. + the response contains the quote and `transaction` is `null`. x-compute-units: 100 x-rate-limit-cus: 100 operationId: get-swap-order @@ -35,7 +35,8 @@ paths: required: false description: > Public key of the wallet that will sign the transaction. When - omitted, the response returns a quote and `transaction` is `null`. + omitted, the response returns a quote only and `transaction` is + `null`. schema: type: string example: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ @@ -53,17 +54,21 @@ paths: - name: swapMode in: query required: false - description: Swap mode. Currently only `ExactIn` is supported. + description: > + Swap mode. `ExactIn` treats `amount` as the input amount and + slippage is applied on the output side; `ExactOut` treats `amount` + as the desired output amount and slippage is applied on the input + side. schema: type: string - enum: [ExactIn] + enum: [ExactIn, ExactOut] default: ExactIn - name: slippageBps in: query required: false description: > - Slippage tolerance in basis points (0-10000). Alchemy picks a value - automatically when omitted. + Slippage tolerance in basis points (0-10000). Metis defaults to 50 + when omitted. schema: type: integer minimum: 0 @@ -73,8 +78,8 @@ paths: in: query required: false description: > - Priority fee in lamports. Overrides the automatic priority fee - Alchemy would otherwise pick. + Priority fee in lamports. Overrides the automatic value Alchemy + would otherwise pick. schema: type: integer - name: jitoTipLamports @@ -98,44 +103,145 @@ paths: required: false description: > Comma-separated list of DEX labels to exclude from routing. Labels - are case-sensitive (e.g. `Raydium,Orca+V2,Meteora+DLMM`). + are case-sensitive (for example, `Raydium,Orca+V2,Meteora+DLMM`). schema: type: string responses: "200": - description: Quote with optional assembled transaction. + description: Quote, plus an assembled transaction when a `taker` was supplied. content: application/json: schema: $ref: "#/components/schemas/OrderResponse" - "400": - description: Malformed request or invalid parameters. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "401": - description: Missing or invalid API key. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "429": - description: Rate limit exceeded. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" + examples: + quoteOnly: + summary: Quote-only (no taker) + value: + mode: manual + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + otherAmountThreshold: "233333336" + swapMode: ExactIn + slippageBps: 50 + priceImpact: -0.05 + priceImpactPct: "-0.0005" + inUsdValue: 234.5 + outUsdValue: 234.2 + swapUsdValue: 234.3 + swapType: aggregator + gasless: false + router: metis + routePlan: + - swapInfo: + ammKey: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 + label: Orca V2 + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + percent: 100 + bps: 10000 + usdValue: 234.3 + transaction: null + taker: null + signatureFeeLamports: 0 + prioritizationFeeLamports: 0 + rentFeeLamports: 0 + signatureFeePayer: null + prioritizationFeePayer: null + rentFeePayer: null + withTransaction: + summary: Quote plus assembled transaction (taker supplied) + value: + mode: manual + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + otherAmountThreshold: "233333336" + swapMode: ExactIn + slippageBps: 50 + priceImpact: -0.05 + priceImpactPct: "-0.0005" + inUsdValue: 234.5 + outUsdValue: 234.2 + swapUsdValue: 234.3 + swapType: aggregator + gasless: false + router: metis + routePlan: + - swapInfo: + ammKey: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 + label: Orca V2 + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + percent: 100 + bps: 10000 + usdValue: 234.3 + transaction: AQAAAA...base64-encoded-unsigned-transaction... + taker: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + lastValidBlockHeight: "300000000" + signatureFeeLamports: 5000 + prioritizationFeeLamports: 500000 + rentFeeLamports: 2039280 + signatureFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + prioritizationFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + rentFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + pricedButUnexecutable: + summary: 200 rejection (priced but transaction could not be built) + value: + mode: manual + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + otherAmountThreshold: "233333336" + swapMode: ExactIn + slippageBps: 50 + priceImpact: -0.05 + priceImpactPct: "-0.0005" + inUsdValue: 234.5 + outUsdValue: 234.2 + swapUsdValue: 234.3 + swapType: aggregator + gasless: false + router: metis + routePlan: + - swapInfo: + ammKey: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 + label: Orca V2 + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + percent: 100 + bps: 10000 + usdValue: 234.3 + transaction: "" + taker: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + signatureFeeLamports: 0 + prioritizationFeeLamports: 0 + rentFeeLamports: 0 + signatureFeePayer: null + prioritizationFeePayer: null + rentFeePayer: null + errorCode: 1 + errorMessage: Insufficient funds + error: Insufficient funds "/{apiKey}/build": get: summary: Build Swap Instructions description: > - Returns raw Solana swap instructions rather than a pre-built transaction. - Use `/build` when you need to compose the swap into a larger transaction - of your own (for example a program deposit before the swap or a - cleanup step after it). If you just need a signable transaction, use - `/order` with a `taker`. + Returns raw Solana swap instructions rather than a pre-built + transaction. Use `/build` when you need to compose the swap into a + larger transaction of your own (for example, a program deposit before + the swap or a cleanup step after it). If you just need a signable + transaction, use `/order` with a `taker`. x-compute-units: 100 x-rate-limit-cus: 100 operationId: build-swap-instructions @@ -154,58 +260,261 @@ paths: schema: type: string example: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ - - name: receiver + - name: payer in: query required: false - description: Public key of the wallet that will receive the output tokens. + description: > + Public key of the wallet that pays signature, priority, and rent + fees. Defaults to `taker` when omitted. schema: type: string - - name: swapMode + - name: slippageBps + in: query + required: false + description: > + Slippage tolerance in basis points (0-10000). Metis defaults to 50 + when omitted. The literal string `rtse` is accepted in place of an + integer to opt into Metis's real-time slippage estimator. + schema: + oneOf: + - type: integer + minimum: 0 + maximum: 10000 + - type: string + enum: [rtse] + example: 50 + - name: dexes in: query required: false - description: Swap mode. Currently only `ExactIn` is supported. + description: > + Comma-separated list of DEX labels to allow-list for routing + (labels are case-sensitive). Mutually exclusive with `excludeDexes`. schema: type: string - enum: [ExactIn] - default: ExactIn - - name: slippageBps + - name: excludeDexes in: query required: false description: > - Slippage tolerance in basis points (0-10000). Alchemy picks a value - automatically when omitted. + Comma-separated list of DEX labels to exclude from routing (labels + are case-sensitive). Mutually exclusive with `dexes`. + schema: + type: string + - name: platformFeeBps + in: query + required: false + description: > + Integrator fee in basis points (0-10000). When set to a positive + value, `feeAccount` is required. schema: type: integer minimum: 0 maximum: 10000 - example: 50 - - name: excludeDexes + - name: feeAccount + in: query + required: false + description: > + Token account that receives the integrator fee. Required when + `platformFeeBps` is positive. + schema: + type: string + - name: maxAccounts + in: query + required: false + description: > + Upper bound on the number of accounts the swap transaction can + reference (1-64). Lower values keep the transaction well within the + Solana account limit at the cost of narrower routing. + schema: + type: integer + minimum: 1 + maximum: 64 + - name: wrapAndUnwrapSol + in: query + required: false + description: > + When `true`, Metis inserts wrap-SOL and unwrap-SOL instructions + around the swap so native SOL can be used directly as input or + output. + schema: + type: boolean + - name: destinationTokenAccount + in: query + required: false + description: > + Explicit destination SPL token account for the output tokens. + Mutually exclusive with `nativeDestinationAccount`. + schema: + type: string + - name: nativeDestinationAccount + in: query + required: false + description: > + Explicit destination wallet for native SOL output. Mutually + exclusive with `destinationTokenAccount`. + schema: + type: string + - name: blockhashSlotsToExpiry + in: query + required: false + description: > + Number of slots the returned blockhash remains valid for (1-300). + Metis types this as `uint8`, so values in the 256-300 range return + `501` rather than `400`. + schema: + type: integer + minimum: 1 + maximum: 300 + - name: tipAmount in: query required: false - description: Comma-separated list of DEX labels to exclude from routing. + description: > + Jito tip amount in lamports (greater than 0). When set, Metis + surfaces a `tipInstruction` in the response for the caller to + include in the final transaction. + schema: + type: integer + minimum: 1 + - name: computeUnitPricePercentile + in: query + required: false + description: > + Priority-fee percentile to target. Only the string values `medium`, + `high`, and `veryHigh` are accepted. A numeric value returns `501`. + When omitted, Metis prices compute units itself. schema: type: string + enum: [medium, high, veryHigh] + - name: mode + in: query + required: false + description: > + Reserved for future routing modes. Only `fast` is currently valid; + any other value returns `400`. Accepting it does not change + routing. + schema: + type: string + enum: [fast] + - name: forJitoBundle + in: query + required: false + description: > + Set to `true` when you plan to submit the swap inside a Jito + bundle. Adjusts instruction ordering and priority-fee handling for + bundle inclusion. + schema: + type: boolean responses: "200": - description: Raw swap instructions. + description: Quote fields plus decomposed swap instructions. content: application/json: schema: $ref: "#/components/schemas/BuildResponse" + example: + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + otherAmountThreshold: "233333336" + swapMode: ExactIn + slippageBps: 50 + priceImpactPct: "-0.0005" + routePlan: + - swapInfo: + ammKey: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 + label: Orca V2 + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + percent: 100 + bps: 10000 + usdValue: 234.3 + setupInstructions: [] + swapInstruction: + programId: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 + accounts: + - pubkey: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + isSigner: true + isWritable: true + data: AQAAAA... + cleanupInstruction: null + computeBudgetInstructions: + - programId: ComputeBudget111111111111111111111111111111 + accounts: [] + data: AgAAAA== + otherInstructions: [] + tipInstruction: null + addressesByLookupTableAddress: + AddrLookupTab1e1111111111111111111111111111: + - EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + - So11111111111111111111111111111111111111112 + blockhashWithMetadata: + blockhash: + [ + 12, + 34, + 56, + 78, + 90, + 12, + 34, + 56, + 78, + 90, + 12, + 34, + 56, + 78, + 90, + 12, + 34, + 56, + 78, + 90, + 12, + 34, + 56, + 78, + 90, + 12, + 34, + 56, + 78, + 90, + 12, + 34, + ] + lastValidBlockHeight: "300000000" + fetchedAt: + secs_since_epoch: 1750000000 + nanos_since_epoch: 0 "400": - description: Malformed request or invalid parameters. + description: > + Malformed request or invalid parameters. Body is + `{"error":""}` only — no `errorCode`, no `requestId`. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - "401": - description: Missing or invalid API key. + "501": + description: > + Parameter typed correctly but out of range. Currently emitted for: + + * `computeUnitPricePercentile` sent as a number instead of one of + `medium` / `high` / `veryHigh`. + * `blockhashSlotsToExpiry` in the 256-300 range (Metis types the + field as `uint8`). content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - "429": - description: Rate limit exceeded. + "502": + description: > + Upstream failure. Emitted for a Metis routing failure, a + priority-fee estimate failure, a lookup-table resolve failure, a + missing blockhash, or a `tipAmount` request that Metis could not + match to a tip instruction. content: application/json: schema: @@ -215,11 +524,12 @@ paths: post: summary: Quote Multiple Output Mints description: > - Returns quotes for a single input mint against up to 32 candidate output - mints in one call. Useful for pricing a basket or comparing routes - across candidate outputs without issuing N separate requests. Each - entry in `quotes` follows the same shape as `/order` without a `taker`, - so responses carry the quote fields only and `transaction` is `null`. + Returns quotes for a single input mint against up to 32 candidate + output mints in one call. Useful for pricing a basket or comparing + outputs without issuing N separate requests. Each quote in the + response carries only `inAmount` and `outAmount`; use `/order` for the + full quote surface (route plan, fees, transaction) on a single mint + pair. x-compute-units: 100 x-rate-limit-cus: 100 operationId: quote-multiple-output-mints @@ -233,27 +543,29 @@ paths: $ref: "#/components/schemas/MultiQuoteRequest" responses: "200": - description: Array of quotes, one per requested output mint. + description: > + An object keyed by output mint (in request order) with the priced + amounts for each, plus a shared `contextSlot`. content: application/json: schema: $ref: "#/components/schemas/MultiQuoteResponse" + example: + quotes: + EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: + inAmount: "1000000000" + outAmount: "234506710" + Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: + inAmount: "1000000000" + outAmount: "234481933" + contextSlot: 300000000 "400": description: > - Malformed request or invalid parameters. Common cause is submitting - more than 32 output mints in `outputMints`. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "401": - description: Missing or invalid API key. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "429": - description: Rate limit exceeded. + Malformed request body or invalid parameters. Body is + `{"error":""}` only. Common causes are submitting more + than 32 output mints, sending `amount` as a JSON number, an + `outputMints` entry equal to `inputMint`, or a duplicate mint in + `outputMints`. content: application/json: schema: @@ -303,7 +615,8 @@ components: required: true description: > Amount to swap in the smallest unit of the input token (lamports for - native SOL, base units for SPL tokens). + native SOL, base units for SPL tokens). On `ExactOut`, this is the + desired output amount instead. schema: type: string default: "1000000000" @@ -311,8 +624,11 @@ components: schemas: OrderResponse: type: object - description: Quote fields, plus an assembled transaction when a taker was supplied. + description: Quote fields plus an assembled transaction when a taker was supplied. properties: + mode: + type: string + description: Router mode used to price the quote. inputMint: type: string description: Mint of the input token, echoed back. @@ -324,14 +640,19 @@ components: description: Input amount used for the quote, in base units of the input mint. outAmount: type: string - description: Estimated output amount before slippage, in base units of the output mint. + description: > + Estimated output amount before slippage, in base units of the + output mint. otherAmountThreshold: type: string - description: Minimum output amount after slippage is applied. + description: > + Slippage threshold, in base units. On `ExactIn` this is the minimum + output amount after slippage; on `ExactOut` this is the maximum + input amount after slippage. Always present. swapMode: type: string description: Swap mode used to build the quote. - enum: [ExactIn] + enum: [ExactIn, ExactOut] slippageBps: type: integer description: Slippage tolerance in basis points. @@ -339,78 +660,185 @@ components: type: number description: > Price impact of the swap, in percentage points (for example, `-0.1` - means -0.1%). Divide by 100 to convert to a decimal fraction. - routePlan: - type: array - description: Ordered list of route steps that make up the swap. - items: - $ref: "#/components/schemas/RoutePlanStep" + means -0.1%). Always present. + priceImpactPct: + type: string + description: > + Price impact as a decimal fraction string. Divide `priceImpact` by + 100 to reconcile the two values. + inUsdValue: + type: number + description: USD value of the input amount at quote time. + outUsdValue: + type: number + description: USD value of the output amount at quote time. + swapUsdValue: + type: number + description: USD value of the swap at quote time. + swapType: + type: string + description: Router category that produced the quote (for example, `aggregator`). + gasless: + type: boolean + description: > + `true` when signature and priority fees are paid by a wallet other + than the taker. router: type: string description: > Router that produced the quote. Alchemy currently serves quotes through Metis only. enum: [metis] + routePlan: + type: array + description: Ordered list of route steps that make up the swap. + items: + $ref: "#/components/schemas/RoutePlanStep" transaction: type: ["string", "null"] description: > - Base64-encoded, unsigned Solana transaction. `null` when `taker` - was not supplied. Empty string when `taker` was supplied but the - router could not build a transaction — inspect `errorCode` and - `errorMessage` in that case. + Base64-encoded, unsigned Solana transaction. Always present. `null` + when `taker` was not supplied. Empty string when `taker` was + supplied but the router could not build a transaction — inspect + `errorCode`, `errorMessage`, and `error` in that case. + taker: + type: ["string", "null"] + description: > + Taker echoed back from the request. Always present. `null` when + `taker` was not supplied. lastValidBlockHeight: type: string - description: Block height beyond which the assembled transaction is no longer valid. + description: > + Block height beyond which the assembled transaction is no longer + valid. Omitted (not `null`) when the response does not carry a + usable transaction. signatureFeeLamports: type: integer - description: Estimated signature fee in lamports. + description: > + Estimated signature fee in lamports. Always present. `0` when + `taker` was not supplied or when the transaction was rejected. prioritizationFeeLamports: type: integer - description: Estimated priority fee plus any Jito tip, in lamports. + description: > + Estimated priority fee plus any Jito tip, in lamports. Always + present. `0` when `taker` was not supplied or when the transaction + was rejected. rentFeeLamports: type: integer description: > Estimated rent fee in lamports (for example, to open the output - token account when it does not already exist). - requestId: - type: string - description: Unique identifier for the request. Include when reporting issues. - taker: + token account when it does not already exist). Always present. `0` + when `taker` was not supplied or when the transaction was + rejected. + signatureFeePayer: type: ["string", "null"] - description: Taker echoed back from the request. `null` when not supplied. + description: > + Public key that pays the signature fee. `null` when `taker` was not + supplied or when the transaction was rejected. + prioritizationFeePayer: + type: ["string", "null"] + description: > + Public key that pays the priority fee and any Jito tip. `null` when + `taker` was not supplied or when the transaction was rejected. + rentFeePayer: + type: ["string", "null"] + description: > + Public key that pays the rent fee. `null` when `taker` was not + supplied or when the transaction was rejected. errorCode: type: integer description: > - Present only when `taker` was supplied and `transaction` is the - empty string. Match on `errorCode` to identify the failure: + Present only on a 200 rejection (`taker` was supplied but + `transaction` is the empty string). Match on `errorCode` to + identify the failure: * `1` — insufficient funds * `2` — insufficient SOL for gas - * `3` — swap below minimum for gasless errorMessage: type: string description: > - Human-readable error description. Present when `errorCode` is set. - Match on `errorCode` rather than parsing this string. + Human-readable error description. Present on a 200 rejection + alongside `errorCode`. Match on `errorCode` rather than parsing + this string. + error: + type: string + description: > + Duplicate of `errorMessage`, kept for compatibility. Present on a + 200 rejection alongside `errorCode`. required: + - mode - inputMint - outputMint - inAmount - outAmount + - otherAmountThreshold - swapMode - slippageBps - - routePlan + - priceImpact + - priceImpactPct + - inUsdValue + - outUsdValue + - swapUsdValue + - swapType + - gasless - router + - routePlan + - transaction + - taker + - signatureFeeLamports + - prioritizationFeeLamports + - rentFeeLamports + - signatureFeePayer + - prioritizationFeePayer + - rentFeePayer BuildResponse: type: object - description: Raw Solana swap instructions. + description: Quote fields plus decomposed swap instructions. properties: + inputMint: + type: string + description: Mint of the input token, echoed back. + outputMint: + type: string + description: Mint of the output token, echoed back. + inAmount: + type: string + description: Input amount used for the quote, in base units of the input mint. + outAmount: + type: string + description: > + Estimated output amount before slippage, in base units of the + output mint. + otherAmountThreshold: + type: string + description: > + Minimum output amount after slippage is applied, in base units of + the output mint. `/build` is `ExactIn` only, so this is always a + minimum output. + swapMode: + type: string + description: > + Swap mode used to build the instructions. `/build` supports + `ExactIn` only. + enum: [ExactIn] + slippageBps: + type: integer + description: Slippage tolerance in basis points applied to the quote. + priceImpactPct: + type: string + description: Price impact as a decimal fraction string. + routePlan: + type: array + description: Ordered list of route steps that make up the swap. + items: + $ref: "#/components/schemas/RoutePlanStep" setupInstructions: type: array description: > Instructions that must run before the swap (for example, create an - associated token account or wrap SOL). + associated token account or wrap SOL). Always present; may be + empty. items: $ref: "#/components/schemas/Instruction" swapInstruction: @@ -423,22 +851,100 @@ components: oneOf: - $ref: "#/components/schemas/Instruction" - type: "null" - addressLookupTableAddresses: + computeBudgetInstructions: type: array - description: Address lookup table accounts the swap references. + description: > + Compute-budget instructions returned by Metis (compute-unit limit, + compute-unit price). Always present; may be empty when Metis + declines to set a budget. items: - type: string - computeUnitLimit: - type: integer - description: Recommended compute unit limit for the transaction. - prioritizationFeeLamports: - type: integer - description: Recommended prioritization fee in lamports. - requestId: - type: string - description: Unique identifier for the request. + $ref: "#/components/schemas/Instruction" + otherInstructions: + type: array + description: > + Additional instructions Metis wants included in the final + transaction (for example, integrator fee transfers). Always + present; may be empty. + items: + $ref: "#/components/schemas/Instruction" + tipInstruction: + description: > + Jito tip instruction to include in the final transaction. Present + only when the request supplied `tipAmount`; otherwise `null`. + oneOf: + - $ref: "#/components/schemas/Instruction" + - type: "null" + addressesByLookupTableAddress: + type: ["object", "null"] + description: > + Address lookup table accounts referenced by the swap, keyed by + lookup-table address. Each value is the ordered list of accounts + resolved from that table. `null` when the route uses no lookup + tables. Never an empty object. + additionalProperties: + type: array + items: + type: string + blockhashWithMetadata: + $ref: "#/components/schemas/BlockhashWithMetadata" required: + - inputMint + - outputMint + - inAmount + - outAmount + - otherAmountThreshold + - swapMode + - slippageBps + - priceImpactPct + - routePlan + - setupInstructions - swapInstruction + - cleanupInstruction + - computeBudgetInstructions + - otherInstructions + - tipInstruction + - addressesByLookupTableAddress + - blockhashWithMetadata + + BlockhashWithMetadata: + type: object + description: > + Recent blockhash Metis fetched for the swap transaction, plus its + expiry and fetch timestamp. + properties: + blockhash: + type: array + description: > + Blockhash as a 32-byte little-endian array of unsigned integers, + NOT base58-encoded. + minItems: 32 + maxItems: 32 + items: + type: integer + minimum: 0 + maximum: 255 + lastValidBlockHeight: + type: string + description: Block height beyond which the blockhash is no longer valid. + fetchedAt: + type: object + description: > + Timestamp Metis fetched the blockhash. Keys are snake_case as + returned by the service. + properties: + secs_since_epoch: + type: integer + description: Seconds since Unix epoch. + nanos_since_epoch: + type: integer + description: Nanoseconds portion of the timestamp. + required: + - secs_since_epoch + - nanos_since_epoch + required: + - blockhash + - lastValidBlockHeight + - fetchedAt Instruction: type: object @@ -479,11 +985,22 @@ components: swapInfo: $ref: "#/components/schemas/SwapInfo" percent: - type: number + type: integer description: Percentage of the total swap routed through this step (0-100). + minimum: 0 + maximum: 100 + bps: + type: integer + description: Basis-points share of the total swap routed through this step (0-10000). + minimum: 0 + maximum: 10000 + usdValue: + type: number + description: Optional USD value of this step at quote time. required: - swapInfo - percent + - bps SwapInfo: type: object @@ -520,14 +1037,17 @@ components: default: So11111111111111111111111111111111111111112 amount: type: string - description: Amount to swap, in base units of the input mint. + description: > + Amount to price, in base units of the input mint (or the desired + output amount when `swapMode` is `ExactOut`). Must be a JSON + string; a JSON number returns `400`. example: "1000000000" default: "1000000000" outputMints: type: array description: > - Mint addresses of the candidate output tokens. Up to 32 entries per - request. + Mint addresses of the candidate output tokens (1-32). Entries must + be unique and none may equal `inputMint`. minItems: 1 maxItems: 32 items: @@ -537,13 +1057,11 @@ components: - Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB swapMode: type: string - enum: [ExactIn] + enum: [ExactIn, ExactOut] default: ExactIn - slippageBps: - type: integer - minimum: 0 - maximum: 10000 - description: Slippage tolerance in basis points applied to each quote. + description: > + Swap mode applied to every quote in the batch. On `ExactOut`, + `amount` is the desired output for each mint independently. required: - inputMint - amount @@ -551,31 +1069,45 @@ components: MultiQuoteResponse: type: object + description: > + Priced amounts for each requested output mint, plus a shared context + slot. The response deliberately omits route plans, fees, transactions, + and per-mint errors — use `/order` for the full quote surface on a + single mint pair. properties: quotes: - type: array + type: object description: > - One quote per requested output mint, in the same order as - `outputMints` in the request. - items: - $ref: "#/components/schemas/OrderResponse" - requestId: - type: string - description: Unique identifier for the request. + Object keyed by output mint (in request order). Each value carries + the priced amounts only. + additionalProperties: + $ref: "#/components/schemas/MultiQuoteEntry" + contextSlot: + type: integer + description: > + Oldest slot any quote in `quotes` reported. Omitted when none of + the quotes carried a slot. required: - quotes + MultiQuoteEntry: + type: object + properties: + inAmount: + type: string + description: Input amount in base units of the input mint. + outAmount: + type: string + description: Priced output amount in base units of the output mint. + required: + - inAmount + - outAmount + ErrorResponse: type: object properties: error: type: string description: Human-readable error description. - errorCode: - type: integer - description: Machine-readable error code, when available. - requestId: - type: string - description: Unique identifier for the request. required: - error From ac653fbf7c0768183ade207f97be79e4d5c00d4a Mon Sep 17 00:00:00 2001 From: alchemy-bot <80712764+alchemy-bot@users.noreply.github.com> Date: Tue, 15 Sep 2026 12:50:35 +0000 Subject: [PATCH 4/5] [docs-agent] Drop 4xx/5xx responses on /build and /quote-multiple-output-mints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matches Vlad's earlier ask for /order — all three operations now document 200 only. Removes 400 + 501 + 502 from /build and 400 from /quote-multiple-output-mints. Also drops the now-unreferenced ErrorResponse component from the source (bundler would strip it via --remove-unused-components anyway, but keeping the source tidy). Refs DOCS-227 --- src/openapi/solana-trader/solana-trader.yaml | 50 -------------------- 1 file changed, 50 deletions(-) diff --git a/src/openapi/solana-trader/solana-trader.yaml b/src/openapi/solana-trader/solana-trader.yaml index 06ddcbe26..d8c8f487b 100644 --- a/src/openapi/solana-trader/solana-trader.yaml +++ b/src/openapi/solana-trader/solana-trader.yaml @@ -489,36 +489,6 @@ paths: fetchedAt: secs_since_epoch: 1750000000 nanos_since_epoch: 0 - "400": - description: > - Malformed request or invalid parameters. Body is - `{"error":""}` only — no `errorCode`, no `requestId`. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "501": - description: > - Parameter typed correctly but out of range. Currently emitted for: - - * `computeUnitPricePercentile` sent as a number instead of one of - `medium` / `high` / `veryHigh`. - * `blockhashSlotsToExpiry` in the 256-300 range (Metis types the - field as `uint8`). - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" - "502": - description: > - Upstream failure. Emitted for a Metis routing failure, a - priority-fee estimate failure, a lookup-table resolve failure, a - missing blockhash, or a `tipAmount` request that Metis could not - match to a tip instruction. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" "/{apiKey}/quote-multiple-output-mints": post: @@ -559,17 +529,6 @@ paths: inAmount: "1000000000" outAmount: "234481933" contextSlot: 300000000 - "400": - description: > - Malformed request body or invalid parameters. Body is - `{"error":""}` only. Common causes are submitting more - than 32 output mints, sending `amount` as a JSON number, an - `outputMints` entry equal to `inputMint`, or a duplicate mint in - `outputMints`. - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorResponse" components: securitySchemes: @@ -1102,12 +1061,3 @@ components: required: - inAmount - outAmount - - ErrorResponse: - type: object - properties: - error: - type: string - description: Human-readable error description. - required: - - error From 91b5f6f2e0a74a380d12ac08cea96953bf4870c4 Mon Sep 17 00:00:00 2001 From: alchemy-bot <80712764+alchemy-bot@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:04:09 +0000 Subject: [PATCH 5/5] [docs-agent] Solana Trader API: payer, referral fees, and mode/fee-broadcast corrections Adds payer, referralFee, referralAccount, excludeRouters to /order and payer, referralFee, referralAccount to /build. Adds totalTime, feeBps, feeMint, platformFee to OrderResponse. Adds priceImpact to BuildResponse. Corrects mode default to ultra with the six manual triggers (payer is not one of them); clarifies priorityFeeLamports/jitoTipLamports/broadcastFeeType pair-and-sum semantics with the 1000-lamport jito floor; documents excludeRouters allow-list; makes inUsdValue/outUsdValue/swapUsdValue omitted (not null, not required) with the fiat-backed tiebreak on swapUsdValue; documents gasless as ready-transaction + non-taker payer only. On /build: rewrites payer to describe rent-and-account-creation rewrite only (not signature or priority; no default to taker); flips slippageBps=rtse to 501 and drops the real-time-estimator claim; makes tipAmount always 501 and clarifies tipInstruction is always null; rewrites computeUnitPricePercentile mapping (medium=25th, high=50th, veryHigh=75th) with the default-Medium fallback and 1M-lamport cap; documents computeBudgetInstructions as one SetComputeUnitPrice only. BlockhashWithMetadata.lastValidBlockHeight is now a JSON number (was string); fetchedAt is stamped by this service. Overview blurbs mention payer and referral fees on /order and /build only. Response codes stay 200 only. Refs DOCS-227 --- .../solana/solana-trader-api-overview.mdx | 4 +- src/openapi/solana-trader/solana-trader.yaml | 465 +++++++++++++++--- 2 files changed, 411 insertions(+), 58 deletions(-) diff --git a/content/api-reference/solana/solana-trader-api-overview.mdx b/content/api-reference/solana/solana-trader-api-overview.mdx index 70f8001a8..0979373e5 100644 --- a/content/api-reference/solana/solana-trader-api-overview.mdx +++ b/content/api-reference/solana/solana-trader-api-overview.mdx @@ -18,8 +18,8 @@ Alchemy routes Trader API requests through [Metis](https://station.jup.ag/blog/j The API exposes three endpoints: -* `GET /order` returns a swap quote. Pass a `taker` to also receive an unsigned base64 transaction; omit `taker` for a quote-only response. -* `GET /build` returns raw swap instructions for composing into your own transaction (multi-leg flows, program deposits, cleanup steps). +* `GET /order` returns a swap quote. Pass a `taker` to also receive an unsigned base64 transaction; omit `taker` for a quote-only response. Optional `payer` compiles a sponsor as the fee payer; optional `referralFee` + `referralAccount` charge an integrator fee. +* `GET /build` returns raw swap instructions for composing into your own transaction (multi-leg flows, program deposits, cleanup steps). Optional `payer` rewrites rent and account-creation instructions onto a sponsor; optional `referralFee` + `referralAccount` charge an integrator fee. * `POST /quote-multiple-output-mints` returns quotes for a single input against up to 32 output mints in one call. ## Endpoint diff --git a/src/openapi/solana-trader/solana-trader.yaml b/src/openapi/solana-trader/solana-trader.yaml index d8c8f487b..68f65442a 100644 --- a/src/openapi/solana-trader/solana-trader.yaml +++ b/src/openapi/solana-trader/solana-trader.yaml @@ -21,7 +21,9 @@ paths: Returns a swap quote for `inputMint` → `outputMint`. When `taker` is supplied, the response also includes an unsigned, base64-encoded Solana transaction the taker can sign and broadcast. When `taker` is omitted, - the response contains the quote and `transaction` is `null`. + the response contains the quote and `transaction` is `null`. Optional + `payer` compiles a sponsor as the fee payer; optional `referralFee` + + `referralAccount` charge an integrator fee. x-compute-units: 100 x-rate-limit-cus: 100 operationId: get-swap-order @@ -48,7 +50,7 @@ paths: differ from `taker`. Expects a wallet address, not a token account. For non-SOL output, tokens are sent to the receiver's associated token account (ATA); a create-ATA instruction is added when the ATA - does not exist. + does not exist. Setting this parameter switches `mode` to `manual`. schema: type: string - name: swapMode @@ -58,7 +60,9 @@ paths: Swap mode. `ExactIn` treats `amount` as the input amount and slippage is applied on the output side; `ExactOut` treats `amount` as the desired output amount and slippage is applied on the input - side. + side. Setting this parameter switches `mode` to `manual`; the + echoed value in the response reflects the effective mode and does + not on its own indicate the caller set it. schema: type: string enum: [ExactIn, ExactOut] @@ -67,8 +71,14 @@ paths: in: query required: false description: > - Slippage tolerance in basis points (0-10000). Metis defaults to 50 - when omitted. + Slippage tolerance in basis points (0-10000 integer). Metis + defaults to 50 when omitted. Setting this parameter switches `mode` + to `manual`; the echoed `slippageBps: 50` on a response is Metis's + default and does not by itself indicate the caller set it. The + literal string `rtse` is not accepted on `/order`; sending it + returns `400 {"error":"slippageBps must be a number between 0 and + 10000"}` (on `/build` the same string returns `501` — see that + operation). schema: type: integer minimum: 0 @@ -78,23 +88,38 @@ paths: in: query required: false description: > - Priority fee in lamports. Overrides the automatic value Alchemy - would otherwise pick. + Priority-fee component of a caller-supplied fee sum. Does not + override the automatic fee on its own — it applies only when + `broadcastFeeType` is also set, and is combined with + `jitoTipLamports` (when supplied) into a single sum. `exactFee` + sends that sum; `maxCap` sends `min(estimate, sum)`. Sent without + `broadcastFeeType`, this parameter is ignored. schema: type: integer - name: jitoTipLamports in: query required: false - description: Jito MEV tip in lamports for faster block inclusion. + description: > + Jito-tip component of a caller-supplied fee sum. Does not override + the automatic fee on its own — it applies only when + `broadcastFeeType` is also set, and is combined with + `priorityFeeLamports` (when supplied) into a single sum. `exactFee` + sends that sum; `maxCap` sends `min(estimate, sum)`. Sent without + `broadcastFeeType`, this parameter is ignored. Values below 1000 + return `400 {"error":"jitoTipLamports must be at least 1000"}`. schema: type: integer + minimum: 1000 - name: broadcastFeeType in: query required: false description: > - Fee cap strategy. `maxCap` treats the supplied fee as a maximum; - `exactFee` sends the exact amount. Ignored when neither - `priorityFeeLamports` nor `jitoTipLamports` is set. + Fee cap strategy applied to the sum of `priorityFeeLamports` + + `jitoTipLamports`. `exactFee` sends that sum; `maxCap` sends + `min(estimate, sum)`. Sent without at least one of + `priorityFeeLamports` or `jitoTipLamports`, this parameter is + ignored. Setting this parameter together with either fee parameter + switches `mode` to `manual`. schema: type: string enum: [maxCap, exactFee] @@ -104,6 +129,100 @@ paths: description: > Comma-separated list of DEX labels to exclude from routing. Labels are case-sensitive (for example, `Raydium,Orca+V2,Meteora+DLMM`). + Setting this parameter switches `mode` to `manual`. + schema: + type: string + - name: excludeRouters + in: query + required: false + description: > + Comma-separated list of routers to exclude. Recognized values are + `metis`, `jupiterz`, `dflow`, and `okx`. Excluding `metis`, or + naming anything outside that set, returns + `400 {"error":"invalid excludeRouters"}`. Alchemy only routes + through Metis today, so excluding only routers this API does not + use is accepted and switches `mode` to `manual` without changing + the route. + schema: + type: string + - name: payer + in: query + required: false + description: > + Optional public key of a sponsor that pays fees on behalf of the + taker. + + When `payer` is set and does not equal `taker`, that account is + compiled as the fee payer (account zero) and the taker remains a + second signer. `signatureFeeLamports` becomes `10000` (two + signers), and all three `*FeePayer` fields in the response name + the sponsor. The sponsor pays signature, priority, associated-token + rent, and the Jito tip. The trade itself stays with the taker: + `inAmount` on `ExactIn`, `otherAmountThreshold` on `ExactOut`. A + native SOL debit moves to the sponsor only when it is exactly one + token-account's rent, or is split when it is the trade amount plus + exactly that rent; any other taker SOL debit produces a `502`, + not a transaction that still bills the taker. + + `gasless: true` is set only when a ready transaction was actually + built with a non-taker `payer`. A `payer` equal to `taker`, a + missing `payer`, a quote with no `taker`, or a 200 rejection + (`transaction: ""`) is not gasless, and `mode` stays whatever it + already was — `payer` does not switch `mode` to `manual`. + + There is no integrator allowlist: any valid pubkey can sponsor. + An unfunded sponsor surfaces as `errorCode: 2` on a 200 rejection, + not a `400`. A malformed key returns + `400 {"error":"Invalid payer"}`. + + With a sponsor, `errorCode: 1` still describes the taker's input + side, `errorCode: 2` describes the sponsor's SOL (signature, + priority, associated-token rent, and any system lamports assigned + to the sponsor), and `errorCode: 3` is still never returned. + schema: + type: string + - name: referralFee + in: query + required: false + description: > + Integrator fee in basis points. Must be an integer in the range + 50-255 and must be sent together with `referralAccount`. + + Errors: + + * Out of range or not a number: `400 {"error":"referralFee must be + a number between 50 and 255"}`. + * One of `referralFee` / `referralAccount` without the other: + `400 {"error":"referralFee and referralAccount must be provided + together"}`. + schema: + type: integer + minimum: 50 + maximum: 255 + - name: referralAccount + in: query + required: false + description: > + Referral project account used to derive the destination fee token + account. Must be sent together with `referralFee`. + + The fee is deposited into the PDA + `["referral_ata", referralAccount, feeMint]` under + `REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3`. The fee mint is the + output mint on `ExactIn` and the input mint on `ExactOut`. That + token account must already exist; this API cannot create it. + + Errors: + + * Missing (uninitialized) fee account: + `400 {"error":"referral account is not initialized"}`. + * One of `referralFee` / `referralAccount` without the other: + `400 {"error":"referralFee and referralAccount must be provided + together"}`. + * Malformed key: `400 {"error":"Invalid referralAccount"}`. + + When both parameters are accepted, the response includes optional + `feeBps`, `feeMint`, and `platformFee` fields. schema: type: string responses: @@ -117,7 +236,7 @@ paths: quoteOnly: summary: Quote-only (no taker) value: - mode: manual + mode: ultra inputMint: So11111111111111111111111111111111111111112 outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v inAmount: "1000000000" @@ -152,10 +271,11 @@ paths: signatureFeePayer: null prioritizationFeePayer: null rentFeePayer: null + totalTime: 42 withTransaction: summary: Quote plus assembled transaction (taker supplied) value: - mode: manual + mode: ultra inputMint: So11111111111111111111111111111111111111112 outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v inAmount: "1000000000" @@ -191,10 +311,57 @@ paths: signatureFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ prioritizationFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ rentFeePayer: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + totalTime: 87 + sponsoredWithReferral: + summary: Sponsored transaction (payer set) with referral fee + value: + mode: ultra + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + otherAmountThreshold: "233333336" + swapMode: ExactIn + slippageBps: 50 + priceImpact: -0.05 + priceImpactPct: "-0.0005" + inUsdValue: 234.5 + outUsdValue: 234.2 + swapUsdValue: 234.3 + swapType: aggregator + gasless: true + router: metis + routePlan: + - swapInfo: + ammKey: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 + label: Orca V2 + inputMint: So11111111111111111111111111111111111111112 + outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + inAmount: "1000000000" + outAmount: "234506710" + percent: 100 + bps: 10000 + usdValue: 234.3 + transaction: AQAAAA...base64-encoded-unsigned-transaction... + taker: GkwFnmMDvn3HGMpJpWBg8tgJxr3NxNvg3AXxvXVPbRGJ + lastValidBlockHeight: "300000000" + signatureFeeLamports: 10000 + prioritizationFeeLamports: 500000 + rentFeeLamports: 2039280 + signatureFeePayer: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM + prioritizationFeePayer: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM + rentFeePayer: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM + feeBps: 100 + feeMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + platformFee: + amount: "2345067" + feeBps: 100 + feeMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + totalTime: 104 pricedButUnexecutable: summary: 200 rejection (priced but transaction could not be built) value: - mode: manual + mode: ultra inputMint: So11111111111111111111111111111111111111112 outputMint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v inAmount: "1000000000" @@ -232,6 +399,7 @@ paths: errorCode: 1 errorMessage: Insufficient funds error: Insufficient funds + totalTime: 63 "/{apiKey}/build": get: @@ -241,7 +409,9 @@ paths: transaction. Use `/build` when you need to compose the swap into a larger transaction of your own (for example, a program deposit before the swap or a cleanup step after it). If you just need a signable - transaction, use `/order` with a `taker`. + transaction, use `/order` with a `taker`. Optional `payer` rewrites + rent and account-creation instructions onto a sponsor; optional + `referralFee` + `referralAccount` charge an integrator fee. x-compute-units: 100 x-rate-limit-cus: 100 operationId: build-swap-instructions @@ -264,17 +434,31 @@ paths: in: query required: false description: > - Public key of the wallet that pays signature, priority, and rent - fees. Defaults to `taker` when omitted. + Optional public key of a sponsor. Rewrites rent and + account-creation instructions onto the sponsor using the same + rules as `/order`'s `payer`: the trade itself stays with the taker + (`inAmount` on `ExactIn`), and a native SOL debit moves to the + sponsor only when it is exactly one token-account's rent (or is + split when it is the trade plus exactly that rent). Any other + taker SOL debit produces a `502`. + + `/build` does NOT default `payer` to `taker`, and `payer` does not + pay signature or priority fees — the caller still chooses account + zero when they compile the transaction, so gas stays with whoever + they put there. A `payer` equal to `taker` therefore changes + nothing. Malformed key: `400 {"error":"Invalid payer"}`. The + `/build` response has no `gasless` field and no fee-payer fields. schema: type: string - name: slippageBps in: query required: false description: > - Slippage tolerance in basis points (0-10000). Metis defaults to 50 - when omitted. The literal string `rtse` is accepted in place of an - integer to opt into Metis's real-time slippage estimator. + Slippage tolerance in basis points (0-10000 integer). Metis + defaults to 50 when omitted. The literal string `rtse` returns + `501 {"error":"slippageBps=rtse is not supported; use a number + between 0 and 10000"}` on `/build`; on `/order` the same string + returns `400` (see that operation). schema: oneOf: - type: integer @@ -304,7 +488,10 @@ paths: required: false description: > Integrator fee in basis points (0-10000). When set to a positive - value, `feeAccount` is required. + value, `feeAccount` is required. Mutually exclusive with + `referralFee` / `referralAccount`; sending them together returns + `400 {"error":"referralFee and referralAccount are mutually + exclusive with platformFeeBps and feeAccount"}`. schema: type: integer minimum: 0 @@ -314,7 +501,11 @@ paths: required: false description: > Token account that receives the integrator fee. Required when - `platformFeeBps` is positive. + `platformFeeBps` is positive. The value is only checked as a valid + pubkey — no PDA derivation is performed. Mutually exclusive with + `referralFee` / `referralAccount`; sending them together returns + `400 {"error":"referralFee and referralAccount are mutually + exclusive with platformFeeBps and feeAccount"}`. schema: type: string - name: maxAccounts @@ -368,9 +559,12 @@ paths: in: query required: false description: > - Jito tip amount in lamports (greater than 0). When set, Metis - surfaces a `tipInstruction` in the response for the caller to - include in the final transaction. + Not supported on `/build`. Any value returns + `501 {"error":"tipAmount is not supported; Jupiter's tip is only + redeemable through tx.jup.ag"}`. This is a Jupiter-native tip and + not a Jito tip; the `tipInstruction` field in the response is + always `null`. If you need a Jito tip, request it on `/order` via + `jitoTipLamports` and set an appropriate `broadcastFeeType`. schema: type: integer minimum: 1 @@ -378,9 +572,22 @@ paths: in: query required: false description: > - Priority-fee percentile to target. Only the string values `medium`, - `high`, and `veryHigh` are accepted. A numeric value returns `501`. - When omitted, Metis prices compute units itself. + Priority-fee percentile to target. Accepted string values are + `medium`, `high`, and `veryHigh`; the server maps them one rung + down from Jupiter's naming: `medium` → 25th percentile, `high` → + 50th percentile, `veryHigh` → 75th percentile. + + When omitted, the server uses the same Alchemy priority-fee + estimate that `/order` uses by default: Medium (50th percentile, + which is Jupiter's `high` rung) — omitting this parameter does not + leave Metis's own price in place. + + A numeric percentile returns + `501 {"error":"numeric computeUnitPricePercentile is not + supported; use medium, high, or veryHigh"}`. A failed estimate + returns `502`. A zero estimate is still emitted as + `SetComputeUnitPrice(0)`. The total priority fee is capped at + 1,000,000 lamports. schema: type: string enum: [medium, high, veryHigh] @@ -403,6 +610,59 @@ paths: bundle inclusion. schema: type: boolean + - name: referralFee + in: query + required: false + description: > + Integrator fee in basis points. Must be an integer in the range + 50-255 and must be sent together with `referralAccount`. Mutually + exclusive with `platformFeeBps` and `feeAccount`. + + Errors: + + * Out of range or not a number: `400 {"error":"referralFee must be + a number between 50 and 255"}`. + * One of `referralFee` / `referralAccount` without the other: + `400 {"error":"referralFee and referralAccount must be provided + together"}`. + * Sent together with `platformFeeBps` or `feeAccount`: + `400 {"error":"referralFee and referralAccount are mutually + exclusive with platformFeeBps and feeAccount"}`. + + The `/build` response does NOT include the `feeBps` or `platformFee` + fields returned by `/order`. + schema: + type: integer + minimum: 50 + maximum: 255 + - name: referralAccount + in: query + required: false + description: > + Referral project account used to derive the destination fee token + account. Must be sent together with `referralFee`. Mutually + exclusive with `platformFeeBps` and `feeAccount`. + + The fee is deposited into the PDA + `["referral_ata", referralAccount, feeMint]` under + `REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3`. On `/build` the fee + mint is always the output mint (Metis's `/build` has no + `swapMode`). That token account must already exist; this API cannot + create it. + + Errors: + + * Missing (uninitialized) fee account: + `400 {"error":"referral account is not initialized"}`. + * One of `referralFee` / `referralAccount` without the other: + `400 {"error":"referralFee and referralAccount must be provided + together"}`. + * Malformed key: `400 {"error":"Invalid referralAccount"}`. + * Sent together with `platformFeeBps` or `feeAccount`: + `400 {"error":"referralFee and referralAccount are mutually + exclusive with platformFeeBps and feeAccount"}`. + schema: + type: string responses: "200": description: Quote fields plus decomposed swap instructions. @@ -418,6 +678,7 @@ paths: otherAmountThreshold: "233333336" swapMode: ExactIn slippageBps: 50 + priceImpact: -0.05 priceImpactPct: "-0.0005" routePlan: - swapInfo: @@ -485,7 +746,7 @@ paths: 12, 34, ] - lastValidBlockHeight: "300000000" + lastValidBlockHeight: 300000000 fetchedAt: secs_since_epoch: 1750000000 nanos_since_epoch: 0 @@ -587,7 +848,15 @@ components: properties: mode: type: string - description: Router mode used to price the quote. + description: > + Router mode used to price the quote. Defaults to `ultra`; becomes + `manual` when the caller sets `slippageBps`, `swapMode`, `receiver`, + `excludeDexes`, `excludeRouters`, or a `broadcastFeeType` together + with `priorityFeeLamports` or `jitoTipLamports`. `payer` does not + change `mode`. The value echoed here reflects the effective mode + only — echoed request defaults such as `slippageBps: 50` or + `swapMode: ExactIn` do not on their own indicate the caller set + them. inputMint: type: string description: Mint of the input token, echoed back. @@ -627,21 +896,32 @@ components: 100 to reconcile the two values. inUsdValue: type: number - description: USD value of the input amount at quote time. + description: > + USD value of the input amount at quote time. Omitted (not `null`) + when the input mint cannot be priced. outUsdValue: type: number - description: USD value of the output amount at quote time. + description: > + USD value of the output amount at quote time. Omitted (not `null`) + when the output mint cannot be priced. swapUsdValue: type: number - description: USD value of the swap at quote time. + description: > + USD value of the swap at quote time. Exactly one of the two legs + (input or output): a fiat-backed mint wins first (`USDC`, `USDT`, + `PYUSD`, `USDS`, `FDUSD`, `EURC`), then SOL, and the input side + wins a tie. Omitted (not `null`) when neither leg can be priced. swapType: type: string description: Router category that produced the quote (for example, `aggregator`). gasless: type: boolean description: > - `true` when signature and priority fees are paid by a wallet other - than the taker. + `true` only when a ready transaction was actually built with a + `payer` that is not the taker. A `payer` equal to `taker`, a + missing `payer`, a quote with no `taker`, or a 200 rejection + (`transaction: ""`) is not gasless, and `mode` stays whatever it + already was. router: type: string description: > @@ -669,13 +949,15 @@ components: type: string description: > Block height beyond which the assembled transaction is no longer - valid. Omitted (not `null`) when the response does not carry a - usable transaction. + valid, as a decimal string. Omitted (not `null`) when the response + does not carry a usable transaction. signatureFeeLamports: type: integer description: > Estimated signature fee in lamports. Always present. `0` when `taker` was not supplied or when the transaction was rejected. + With a non-taker `payer` (sponsor), this is `10000` because both + the sponsor and the taker sign. prioritizationFeeLamports: type: integer description: > @@ -693,17 +975,59 @@ components: type: ["string", "null"] description: > Public key that pays the signature fee. `null` when `taker` was not - supplied or when the transaction was rejected. + supplied or when the transaction was rejected. When a non-taker + `payer` is set, this names the sponsor. prioritizationFeePayer: type: ["string", "null"] description: > Public key that pays the priority fee and any Jito tip. `null` when - `taker` was not supplied or when the transaction was rejected. + `taker` was not supplied or when the transaction was rejected. When + a non-taker `payer` is set, this names the sponsor. rentFeePayer: type: ["string", "null"] description: > Public key that pays the rent fee. `null` when `taker` was not - supplied or when the transaction was rejected. + supplied or when the transaction was rejected. When a non-taker + `payer` is set, this names the sponsor. + totalTime: + type: integer + description: > + Time in milliseconds this service spent producing the response. + Always present. + feeBps: + type: integer + description: > + Requested referral fee in basis points, echoed back. Present only + when the request included a valid `referralFee` + `referralAccount` + pair; omitted otherwise. + feeMint: + type: string + description: > + Mint of the token in which the referral fee is charged. On + `ExactIn` this is the output mint; on `ExactOut` this is the input + mint. Present only when the request included a valid `referralFee` + + `referralAccount` pair; omitted otherwise. + platformFee: + type: object + description: > + Details of the integrator fee actually charged. Present only when + the request included a valid `referralFee` + `referralAccount` + pair; omitted otherwise. + properties: + amount: + type: string + description: Fee amount in the smallest unit of `feeMint`. + feeBps: + type: integer + description: Referral fee in basis points, echoed back. + feeMint: + type: string + description: > + Mint of the token in which the fee is charged. Optional inside + `platformFee`; when present, matches the top-level `feeMint`. + required: + - amount + - feeBps errorCode: type: integer description: > @@ -711,8 +1035,16 @@ components: `transaction` is the empty string). Match on `errorCode` to identify the failure: - * `1` — insufficient funds - * `2` — insufficient SOL for gas + * `1` — insufficient funds. Always describes the taker's input + side, even when a `payer` is set. + * `2` — insufficient SOL for gas. Without a sponsor this is the + taker's SOL; with a `payer`, it describes the sponsor's SOL + (signature, priority, associated-token rent, and any system + lamports assigned to the sponsor). + + Code `3` is never returned by this service. Any other unexpected + taker SOL debit surfaces as a `502`, not a 200 rejection that + still bills the taker. errorMessage: type: string description: > @@ -735,9 +1067,6 @@ components: - slippageBps - priceImpact - priceImpactPct - - inUsdValue - - outUsdValue - - swapUsdValue - swapType - gasless - router @@ -750,6 +1079,7 @@ components: - signatureFeePayer - prioritizationFeePayer - rentFeePayer + - totalTime BuildResponse: type: object @@ -784,9 +1114,17 @@ components: slippageBps: type: integer description: Slippage tolerance in basis points applied to the quote. + priceImpact: + type: number + description: > + Price impact of the swap, in percentage points (for example, `-0.1` + means -0.1%). Same number as `/order`'s `priceImpact`. Always + present. priceImpactPct: type: string - description: Price impact as a decimal fraction string. + description: > + Price impact as a decimal fraction string. `priceImpact` divided + by 100. Always present. routePlan: type: array description: Ordered list of route steps that make up the swap. @@ -813,9 +1151,16 @@ components: computeBudgetInstructions: type: array description: > - Compute-budget instructions returned by Metis (compute-unit limit, - compute-unit price). Always present; may be empty when Metis - declines to set a budget. + Priority-fee instructions this service assembles for the caller — + not Metis's own compute-budget instructions and not a compute-unit + limit. The array is exactly one `SetComputeUnitPrice` instruction + and never a `SetComputeUnitLimit`. Omitting + `computeUnitPricePercentile` on the request does not leave Metis's + own price in place: it uses the same Alchemy priority-fee estimate + `/order` uses by default (Medium, 50th percentile, Jupiter's `high` + rung). A zero estimate is still emitted as + `SetComputeUnitPrice(0)`. The total priority fee is capped at + 1,000,000 lamports. items: $ref: "#/components/schemas/Instruction" otherInstructions: @@ -828,8 +1173,10 @@ components: $ref: "#/components/schemas/Instruction" tipInstruction: description: > - Jito tip instruction to include in the final transaction. Present - only when the request supplied `tipAmount`; otherwise `null`. + Reserved for a Jupiter-native tip. Always `null` on `/build` + because `tipAmount` is not supported here (see the `tipAmount` + parameter). If you need a Jito tip, use `/order`'s + `jitoTipLamports` instead. oneOf: - $ref: "#/components/schemas/Instruction" - type: "null" @@ -854,6 +1201,7 @@ components: - otherAmountThreshold - swapMode - slippageBps + - priceImpact - priceImpactPct - routePlan - setupInstructions @@ -869,7 +1217,8 @@ components: type: object description: > Recent blockhash Metis fetched for the swap transaction, plus its - expiry and fetch timestamp. + expiry and the timestamp this service stamped when building the + response. properties: blockhash: type: array @@ -883,13 +1232,17 @@ components: minimum: 0 maximum: 255 lastValidBlockHeight: - type: string - description: Block height beyond which the blockhash is no longer valid. + type: integer + description: > + Block height beyond which the blockhash is no longer valid, as a + JSON number. (On `/order`'s top-level response the equivalent + `lastValidBlockHeight` is a decimal string; on `/build` here it is + a number.) fetchedAt: type: object description: > - Timestamp Metis fetched the blockhash. Keys are snake_case as - returned by the service. + Timestamp this service stamps when building the response. Keys are + snake_case as returned. properties: secs_since_epoch: type: integer