Sending multiple transactions
Plan and execute work across several transactions
When an operation cannot fit in a single transaction — too many instructions, too much data, or work that should run in parallel — you need to split it across several transactions. Kit clients support this through the same client.sendTransactions(...) and client.planTransactions(...) methods you used in Sending transactions, but plural.
Prerequisites
This guide assumes a transaction-ready client, like the one set up in Sending transactions. The same bundle plugins from @solana/kit-plugin-rpc and @solana/kit-plugin-litesvm install everything client.sendTransactions(...) needs out of the box.
Instruction plans
An instruction plan describes an operation made of several instructions together with constraints on how they can run — some steps must happen sequentially, others may run in parallel, and some must stay atomic. Kit represents this as a tree, so simple operations stay simple while more complex flows can nest sequential and parallel branches inside each other. The plan is then handed to a transaction planner that decides how to pack it into transaction messages, and then to a transaction plan executor that sends those transactions.
Kit's planners can divide work, batch it into transactions, and even pack variable-sized data into the available space. For most cases you only need to know about three building blocks: singleInstructionPlan, sequentialInstructionPlan, and parallelInstructionPlan. The Instruction plans advanced guide goes much deeper.
Run work sequentially
Use sequentialInstructionPlan(...) when each step depends on the previous one — for example, creating an account before initializing it.
Sequential plans accept either raw instructions or other instruction plans, so you can compose larger flows from smaller ones. A nonDivisibleSequentialInstructionPlan(...) variant also exists for steps that must run atomically inside a single transaction (or transaction bundle when not possible).
Run work in parallel
Use parallelInstructionPlan(...) when independent steps can run in any order. The planner is then free to pack them into separate transactions and send them concurrently.
The two helpers compose freely: a sequential plan can contain parallel children and vice versa. This is how you describe operations like "set up these two accounts in parallel, then run the operation that depends on both of them".
Plan multiple transactions
When you only want to inspect or persist the planned transactions, client.planTransactions(plan) runs the planner without executing anything and returns the resulting TransactionPlan tree.
The returned TransactionPlan keeps the same sequential and parallel structure as the original instruction plan, but its leaves are the concrete transaction messages the planner chose to pack the work into. This is useful for dry runs, debugging, or feeding a custom executor.
Send multiple transactions
To plan and send in one call, use client.sendTransactions(plan). The result is a TransactionPlanResult tree that mirrors the original plan, with one leaf per transaction.
client.sendTransactions(...) is the multi-transaction counterpart of client.sendTransaction(...). The single-transaction version asserts that the plan resolves to exactly one transaction and unwraps its result — a convenience for the common case. The plural version preserves the full tree, which you can walk to see what happened.
Understand transaction plan results
Every leaf in the result tree is a SingleTransactionPlanResult with one of three statuses:
successful— the transaction was sent and confirmed;context.signatureis always present.failed— the transaction was attempted but failed during preflight, simulation, or execution.errorcarries the underlying error.canceled— the transaction was never attempted, typically because a prior transaction in a sequential branch failed first.
Branch nodes use kind: 'parallel' or kind: 'sequential', with their child results in plans. You usually do not need to walk the tree by hand — the helpers in the next sections cover the common cases.
Summarize results
summarizeTransactionPlanResult(...) flattens the tree and bucketizes the results so you can quickly check whether everything succeeded.
This is usually the first thing to reach for after client.sendTransactions(...) — it gives you a quick health check without writing any tree-walking code.
Continue after failures
By default, client.sendTransactions(...) throws as soon as any transaction in the plan fails. That behaviour is convenient for small operations, but counterproductive when you are running a large batch and want to inspect partial results. The passthroughFailedTransactionPlanExecution(...) helper turns that thrown error back into a TransactionPlanResult.
After this call, result is the same tree you would have received on full success — but with failed and canceled leaves where the issues occurred. You can then summarize it, walk it, or react however you need.
Handle partial success
When you want to react to each transaction individually, flattenTransactionPlanResult(...) collapses the tree into an array of leaf results in the order they appear.
Combined with passthroughFailedTransactionPlanExecution(...), this is enough to drive a UI that shows per-transaction status, retry buttons, or detailed error messages for a long-running multi-transaction operation.
Next steps
- Sending transactions — single-transaction basics.
- Advanced guides — Instruction plans — the full instruction plan API.
- Advanced guides — Errors — recover from
SolanaErrorfailures.