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

# Get batch transaction

> Returns the Solana transactions that pay every deposit address in the group.

* every order must send the **same token** — all native SOL, or all the same SPL token. Mixing SOL with an SPL token is rejected
* sign the returned transactions and broadcast them yourself. Nothing to submit back
* large groups come back as several transactions, so handle every entry in `transactions`

Find batchable tokens with [Get tokens](https://docs.houdini.com/api-reference/split/get-tokens.md) and `hasBundler=true`. See the [Split flow](https://docs.houdini.com/developer-hub/swap-flows/split.md) for the full sequence.

## Choosing a transaction format

Solana has more than one transaction format. Use `txVersion` to choose which one you get back. If you are not sure, leave it out and you get `legacy`.

| `txVersion` | Deposits per transaction | Who can sign it |
| - | - | - |
| `legacy` (default) | up to 20 in SOL, 10 in a token | every Solana wallet and library |
| `v0` | up to 20 in SOL, 10 in a token | most wallets, and libraries that support versioned transactions |
| `v1` | up to 60 in SOL, 10 in a token | only some wallets so far. In code, `@solana/kit` 8.0 or later |

**Why use `v1`?** Each transaction pays 3 times more SOL deposits, so the user signs fewer times. For example, 50 SOL deposits take 3 transactions with `legacy` but only 1 with `v1`. Token batches stay at 10 in every format: creating the receiving token accounts hits a Solana limit before the size limit does.

**When not to use `v1`:** if the wallet or library that signs does not support it, signing fails. `@solana/web3.js` 1.x can read a `v1` transaction but cannot sign it.

To check support, see Solana's [Larger Transaction Sizes](https://solana.com/upgrades/larger-transaction-sizes) guide:

* **Libraries** — the "Support for V1 Transactions" table lists the minimum version of each Solana library
* **Wallets** — a wallet that can sign `v1` lists `1` in its `supportedTransactionVersions`. Check it before you request `txVersion=v1`

The response's `txVersion` field tells you which format you got.

### Signing and sending

* **`legacy`** — decode with `Transaction.from(Buffer.from(txData.data, "base64"))` from `@solana/web3.js`
* **`v0`** — decode with `VersionedTransaction.deserialize(Buffer.from(txData.data, "base64"))` from `@solana/web3.js`
* **`v1`** — decode and sign with a library that supports `v1`, then send with `encoding: "base64"`. A `v1` transaction can be larger than 1232 bytes, and base58 encoding cannot carry it. See Solana's [Larger Transaction Sizes](https://solana.com/upgrades/larger-transaction-sizes) guide for examples


## OpenAPI

````yaml GET /exchanges/multi/{multiId}/tx
openapi: 3.0.0
info:
  title: Houdini Partner API
  version: 2.1.2
  description: >-
    Quote and execute private, standard, and DEX swaps — one at a time or
    batched.
  license:
    name: ISC
  contact: {}
servers:
  - url: https://api-partner.houdiniswap.com/v2
security: []
tags:
  - name: Private and Standard Swaps
    description: >-
      CEX swaps in two modes, sharing the same endpoints — pick one with the
      `types` parameter.


      - `types=standard` — direct single-leg swap. Fastest, no privacy hop.

      - `types=private` — two legs through a privacy bridge, so deposit and
      payout are not linkable on-chain. Adds `anonymous`, `useXmr`,
      `rotatePayoutWallets` and the `inLeg*` / `outLeg*` filters.


      Flow: [Get tokens](/api-reference/private-and-standard-swaps/get-tokens)
      with `hasCex=true` → [Get
      quotes](/api-reference/private-and-standard-swaps/get-quotes) → [Create
      exchange](/api-reference/private-and-standard-swaps/create-exchange) →
      [track the
      order](/api-reference/private-and-standard-swaps/get-order-details).
      `markup` works in both modes.
  - name: Private Send
    description: >-
      Send a token to another wallet privately — same token in and out, with no
      on-chain link between the two addresses.


      - find eligible tokens with [Get
      tokens](/api-reference/private-send/get-tokens) and `hasSelfPrivate=true`

      - quote with Get private send quote, then create with Create private send
      order

      - you can also stay on the main endpoints with a private same-token
      `quoteId`


      See the [Private send](/docs/v2/private-send) guide.
  - name: Split
    description: >-
      Create and track a group of swaps sharing one `multiId`.


      - post the orders straight to Create split — there is no quote step and no
      `quoteId`

      - Get split quotes is optional, for showing rates first

      - each order gets its own deposit address; poll [Get batch
      status](/api-reference/split/get-batch-status) for every leg

      - CEX standard and private only — no DEX in a batch


      To fund the whole group in one transaction, see Bundler SOL or Bundler
      EVM. See the [Split flow](/developer-hub/swap-flows/split) for the full
      sequence.
  - name: Bundler SOL
    description: >-
      Fund a whole Split group with one Solana batch transfer.


      - one call — Get batch transaction returns the transaction to sign and
      broadcast

      - every order must send the same token

      - deposits per batch depend on `txVersion`: up to 20 SOL or 10 token
      deposits with `legacy` or `v0`, up to 60 SOL or 10 token deposits with
      `v1`

      - find batchable tokens with [Get tokens](/api-reference/split/get-tokens)
      and `hasBundler=true`

      - nothing to submit back afterwards — retry and recovery are EVM-only


      See the [Split flow](/developer-hub/swap-flows/split) for the full
      sequence.
  - name: Bundler EVM
    description: >-
      Fund a whole Split group with one ERC-4337 UserOperation, submitted
      through the bundler.


      - Ethereum, Base and BNB Smart Chain only; other EVM chains are rejected

      - up to 50 orders per group, funded in one transaction; every order must
      send the same source token

      - find batchable tokens with [Get tokens](/api-reference/split/get-tokens)
      and `hasBundler=true`

      - build the UserOp, then submit the signed result

      - if a bundle fails: retry within 30 minutes, or recover stuck assets


      See the [Split flow](/developer-hub/swap-flows/split) for the full
      sequence.
  - name: On-chain DEX or Bridge
    description: >-
      On-chain swaps signed and funded from the user's own wallet.


      Flow: [Get tokens](/api-reference/on-chain-dex-or-bridge/get-tokens) with
      `hasDex=true` → [Get
      quotes](/api-reference/on-chain-dex-or-bridge/get-quotes) with `types=dex`
      → [Get token approval
      data](/api-reference/on-chain-dex-or-bridge/get-token-approval-data) →
      send it and poll [Check token
      allowance](/api-reference/on-chain-dex-or-bridge/check-token-allowance) →
      [Get next chain
      signature](/api-reference/on-chain-dex-or-bridge/get-next-chain-signature)
      → [Create exchange](/api-reference/on-chain-dex-or-bridge/create-exchange)
      → [Confirm DEX
      transaction](/api-reference/on-chain-dex-or-bridge/confirm-dex-transaction)
      → [track the
      order](/api-reference/on-chain-dex-or-bridge/get-order-details).


      - `slippage` applies to DEX only

      - `markup` is not supported
  - name: Reference Data
    description: >-
      Optional lookups: supported chains, liquidity providers, and per-pair
      min/max swap amounts. Responses are cached and change rarely — safe to
      fetch once and reuse. Token ids come from `/tokens`, listed in each swap
      section above.
  - name: Partner Account
    description: >-
      Your orders, profile, earned commissions, withdrawals, volume analytics,
      and the rate-limit budget attached to your API key. Every endpoint is
      scoped to the partner that owns the key making the request.
  - name: System
    description: >-
      Service health. [Get system
      health](/api-reference/system/get-system-health) is unauthenticated and
      exempt from x402 payment — use it for uptime monitoring.
paths:
  /exchanges/multi/{multiId}/tx:
    get:
      tags:
        - Bundler SOL
      summary: Get batch transaction
      description: >-
        Returns the Solana transactions that pay every deposit address in the
        group. This is the only call a Solana batch needs — sign each returned
        transaction and broadcast it yourself, then poll [Get batch
        status](/api-reference/split/get-batch-status).


        - Solana only. For EVM use [Build batch
        transaction](/api-reference/bundler-evm/build-batch-transaction)

        - every order must send the same token — all native SOL, or all the same
        SPL token

        - one transaction pays up to 20 deposits in SOL or 10 in a token. With
        `txVersion=v1` it pays up to 60 in SOL; tokens stay at 10

        - a larger group comes back as several entries in `transactions`. Sign
        and broadcast every one

        - `txData.data` is the unsigned transaction, base64 encoded

        - nothing to submit back afterwards
      operationId: GetMultiExchangeTxData
      parameters:
        - description: The Split group ID
          in: path
          name: multiId
          required: true
          schema:
            type: string
            maxLength: 64
          example: mx8Qa1
        - description: Wallet address that will sign and fund the transaction(s)
          in: query
          name: sender
          required: true
          schema:
            type: string
            minLength: 1
          example: 8jZnXYnZB1MJQG6zBXyouzcycsaPTrZtPXPHDNifjYaC
        - in: query
          name: houdiniIds
          required: false
          schema:
            type: string
        - description: >-
            Transaction format to build. If you are not sure, leave it out.

            - `legacy` (default): works with every Solana wallet

            - `v0`: same capacity as `legacy`. Use it if your code expects a
            versioned transaction

            - `v1`: fits 3 times more SOL deposits in one transaction, so the
            user signs fewer times. Token batches stay the same size. Use it
            only if the wallet or library that signs supports v1. See [Choosing
            a transaction
            format](/api-reference/bundler-sol/get-batch-transaction#choosing-a-transaction-format)
          in: query
          name: txVersion
          required: false
          schema:
            $ref: '#/components/schemas/SolanaTxVersion'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MultiExchangeTxResult'
              examples:
                Example 1:
                  value:
                    multiId: 8ppABEC1HZGrSr1PGbUnCN
                    chain: sol
                    txVersion: legacy
                    transactions:
                      - houdiniIds:
                          - nkuYKXXrfzabgG4u3nZhpq
                          - q2R66TDXHeHKb5kxEcAo9C
                        txData:
                          data: >-
                            AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB...
        '401':
          description: >-
            Credentials missing, malformed, or rejected — all return
            `INVALID_API_CREDENTIALS`. Sending only `partner-id` returns
            `PARTNER_ID_NOT_SUPPORTED`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '402':
          description: >-
            Payment required — x402 protocol. The body is empty; what to pay is
            in the `payment-required` header. Not returned when you send an
            `Authorization` header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/X402PaymentRequired'
          headers:
            payment-required:
              schema:
                type: string
              required: true
        '403':
          description: Access Denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
        '429':
          description: Rate limit exceeded — back off and retry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKey: []
components:
  schemas:
    SolanaTxVersion:
      enum:
        - legacy
        - v0
        - v1
      type: string
    MultiExchangeTxResult:
      properties:
        multiId:
          type: string
          description: The group these transactions fund.
        chain:
          type: string
          description: Chain kind of the from token (e.g. "solana", "evm")
        txVersion:
          $ref: '#/components/schemas/SolanaTxVersion'
          description: >-
            Solana only. Format of every transaction in `transactions`. Matches
            the `txVersion` you requested, or `legacy` if you did not set one.
        transactions:
          items:
            $ref: '#/components/schemas/TxBatch'
          type: array
          description: >-
            One entry per batch. A group larger than the per-chain batch limit
            is divided across several.
        depositNeeded:
          type: string
          description: >-
            Native coin the smart account still needs before you can submit, in
            wei. `"0"` means it is already funded.
        saCurrentBalance:
          type: string
          description: >-
            Native coin the smart account holds right now, in wei. The account
            is owned by the `sender` wallet, so this balance stays under the
            user's control.
      required:
        - multiId
        - chain
        - transactions
      type: object
      additionalProperties: false
    ErrorResponse:
      properties:
        message:
          type: string
        code:
          type: string
          description: >-
            Machine-readable reason for the failure. Branch on this, not on
            `message` — messages are free to change.


            The value depends on the HTTP status. The common ones:


            - `400` — `BAD_REQUEST`

            - `401` — `INVALID_API_CREDENTIALS`, `PARTNER_ID_NOT_SUPPORTED`

            - `403` — `ACCESS_DENIED`, `FIXED_RATE_NOT_ALLOWED`

            - `404` — `NOT_FOUND`, `TOKEN_NOT_FOUND`, `TOKEN_DISABLED`

            - `409` — `ALREADY_SUBMITTED`, `STATIC_DEPOSIT_IN_USE`

            - `422` — `VALIDATION_ERROR`, `AMOUNT_TOO_LOW`, `INVALID_SWAP`,
            `FIXED_RATE_QUOTE_EXPIRED`, and most other request problems

            - `429` — `RATE_LIMIT_EXCEEDED`

            - `5xx` — `INTERNAL_SERVER_ERROR`, `PROVIDER_EXCHANGE_FAILED`,
            `SERVICE_UNAVAILABLE`


            New codes are added over time, so treat this as an open set: match
            the ones you handle and fall back to the HTTP status for the rest.
        requestId:
          type: string
          description: >-
            Unique id for this request. Quote it when contacting support — it is
            how we find the failure in our logs.
      required:
        - message
        - code
      type: object
      additionalProperties: false
    X402PaymentRequired:
      description: >-
        Body of a `402 Payment required`. Empty — `{}` — for API clients; a
        request that looks like a browser gets an HTML paywall page instead.


        This is the x402 payment protocol, not the normal error format, so there
        is no `code` or `message` to branch on. What to pay is always in the
        `payment-required` response header, in both cases: base64-encoded JSON
        whose `accepts[]` entries each carry the `network`, the `asset` to pay
        in, the `amount`, and the `payTo` address, alongside `maxTimeoutSeconds`
        and the x402 `scheme`.


        Sending `Authorization` skips the payment flow, so a 402 only reaches
        callers paying per request. An `apiKey` query param also skips it but is
        not a credential — those requests fail with `401
        INVALID_API_CREDENTIALS`.
      properties: {}
      type: object
      additionalProperties: false
    ValidationError:
      properties:
        message:
          type: string
        code:
          type: string
          description: >-
            Machine-readable reason for the failure. Branch on this, not on
            `message` — messages are free to change.


            The value depends on the HTTP status. The common ones:


            - `400` — `BAD_REQUEST`

            - `401` — `INVALID_API_CREDENTIALS`, `PARTNER_ID_NOT_SUPPORTED`

            - `403` — `ACCESS_DENIED`, `FIXED_RATE_NOT_ALLOWED`

            - `404` — `NOT_FOUND`, `TOKEN_NOT_FOUND`, `TOKEN_DISABLED`

            - `409` — `ALREADY_SUBMITTED`, `STATIC_DEPOSIT_IN_USE`

            - `422` — `VALIDATION_ERROR`, `AMOUNT_TOO_LOW`, `INVALID_SWAP`,
            `FIXED_RATE_QUOTE_EXPIRED`, and most other request problems

            - `429` — `RATE_LIMIT_EXCEEDED`

            - `5xx` — `INTERNAL_SERVER_ERROR`, `PROVIDER_EXCHANGE_FAILED`,
            `SERVICE_UNAVAILABLE`


            New codes are added over time, so treat this as an open set: match
            the ones you handle and fall back to the HTTP status for the rest.
        requestId:
          type: string
          description: >-
            Unique id for this request. Quote it when contacting support — it is
            how we find the failure in our logs.
        fields:
          $ref: '#/components/schemas/FieldErrors'
          description: >-
            The inputs that failed, keyed by field name — or by path for a
            nested field, e.g. `filters.rotatePayoutWallets`. Each entry has a
            `message` and, when you sent one, the `value`.


            Not every `422` names its input, so `fields` can be `{}`: branch on
            `code`, and use `fields` to highlight the input when one is named.
      required:
        - message
        - code
        - fields
      type: object
      additionalProperties: false
    TxBatch:
      properties:
        houdiniIds:
          items:
            type: string
          type: array
          description: HoudiniIds of the orders included in this batch
        txData:
          anyOf:
            - $ref: '#/components/schemas/EvmBatchTxSlimData'
            - $ref: '#/components/schemas/BatchTxData'
          description: Network-specific transaction data ready to broadcast
      required:
        - houdiniIds
        - txData
      type: object
      additionalProperties: false
    FieldErrors:
      properties: {}
      type: object
      additionalProperties:
        properties:
          value: {}
          message:
            type: string
        required:
          - message
        type: object
    EvmBatchTxSlimData:
      description: The fields needed to sign and submit the batch.
      properties:
        userOpHash:
          type: string
          description: >-
            The digest the user's wallet must sign, as 32 bytes of hex. Sign it
            as a message — `signMessage({ message: { raw: userOpHash } })` in
            viem, `signMessage(getBytes(userOpHash))` in ethers — then post the
            signature to [Submit signed
            transaction](/api-reference/bundler-evm/submit-signed-transaction).
        to:
          type: string
          description: >-
            Where to send the funding transaction. It is **not** always the
            smart account:


            - ERC-20 source token — the token contract. The smart account is the
            recipient encoded inside `data`.

            - native source coin — the smart account itself.
        data:
          type: string
          description: >-
            Calldata for the funding transaction. An ERC-20 `transfer` moving
            the tokens to the smart account, or `0x` when the source token is
            the chain's native coin.


            The amount encoded here is the full balance the batch needs.
        value:
          type: string
          description: >-
            Native coin to send with the transaction, in wei. Covers gas plus
            the swap amount when the source token is the native coin, and is
            `"0"` for ERC-20 sources.
        chainId:
          type: number
          format: double
          description: EVM chain the batch runs on, e.g. `1` for Ethereum, `8453` for Base.
        tokenAmount:
          type: string
          description: >-
            How much of the source token still has to reach the smart account,
            in the token's smallest unit. Already funded balance is deducted.
            `"0"` when the source token is the chain's native coin.
      required:
        - userOpHash
        - to
        - data
        - value
        - chainId
        - tokenAmount
      type: object
      additionalProperties: false
    BatchTxData:
      anyOf:
        - $ref: '#/components/schemas/SolanaBatchTxData'
        - $ref: '#/components/schemas/EvmBatchTxData'
      description: Union — add new network types here as they are supported
    SolanaBatchTxData:
      properties:
        data:
          type: string
          description: Serialized transaction bytes (base64)
      required:
        - data
      type: object
      additionalProperties: false
    EvmBatchTxData:
      properties:
        userOp:
          $ref: '#/components/schemas/EvmUserOp'
        userOpHash:
          type: string
        chainId:
          type: number
          format: double
        entryPoint:
          type: string
        to:
          type: string
        smartAccountAddress:
          type: string
        gasEstimate:
          $ref: '#/components/schemas/EvmGasEstimate'
        data:
          type: string
        value:
          type: string
        tokenAmount:
          type: string
      required:
        - userOp
        - userOpHash
        - chainId
        - entryPoint
        - to
        - smartAccountAddress
        - gasEstimate
        - data
        - value
        - tokenAmount
      type: object
      additionalProperties: false
    EvmUserOp:
      description: >-
        EntryPoint v0.7 unpacked UserOp. User pays gas from Smart Account
        balance — no paymaster.
      properties:
        sender:
          type: string
        nonce:
          type: string
        factory:
          type: string
        factoryData:
          type: string
        callData:
          type: string
        callGasLimit:
          type: string
        verificationGasLimit:
          type: string
        preVerificationGas:
          type: string
        maxFeePerGas:
          type: string
        maxPriorityFeePerGas:
          type: string
        signature:
          type: string
        paymaster:
          type: string
        paymasterData:
          type: string
        paymasterVerificationGasLimit:
          type: string
        paymasterPostOpGasLimit:
          type: string
      required:
        - sender
        - nonce
        - callData
        - callGasLimit
        - verificationGasLimit
        - preVerificationGas
        - maxFeePerGas
        - maxPriorityFeePerGas
        - signature
      type: object
      additionalProperties: false
    EvmGasEstimate:
      properties:
        gasCostWei:
          type: string
        gasCostNative:
          type: string
        nativeSymbol:
          type: string
        totalTransferAmountWei:
          type: string
        requiredSmartAccountBalanceWei:
          type: string
        gasCostToken:
          type: string
        gasCostTokenDecimals:
          type: number
          format: double
        exchangeRate:
          type: string
        paymasterStub:
          type: boolean
      required:
        - gasCostWei
        - gasCostNative
        - nativeSymbol
        - totalTransferAmountWei
        - requiredSmartAccountBalanceWei
      type: object
      additionalProperties: false
  securitySchemes:
    apiKey:
      type: apiKey
      name: Authorization
      in: header
      description: >-
        Send `Authorization: <ApiKey>:<ApiSecret>` — joined by a colon, raw. No
        `Bearer` prefix, no base64.


        [Get your
        credentials](https://docs.houdini.com/developer-hub/getting-started/authentication)

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.