Flashbots Bundles Explained: How to Send eth_sendBundle and Why Yours Isn't Landing
Almost every MEV bot on Ethereum, whether it's an arbitrage bot, a sniper or a rescue script, ends up sending bundles. And almost every developer who sends their first bundle has the same experience: the request returns a bundle hash, everything looks fine, and nothing ever shows up on-chain.
This guide covers what a bundle is, how to send one properly, and a practical checklist for figuring out why yours isn't landing.
What a bundle is
A bundle is an ordered list of signed transactions that you send privately to a block builder instead of broadcasting them to the public mempool. The builder will include the whole bundle, in exactly that order, in one block, or it won't include any of it.
Flashbots popularized this. Their Quick Start describes the flow: searchers send bundles to builders, builders simulate them to make sure they don't revert, build full blocks, and deliver those blocks to validators through mev-boost relays. Validators running mev-boost pick the most valuable block offered to them.
Three properties make bundles so useful:
- Atomic. All of the transactions land together or none do. That's what makes things like sponsored wallet rescues possible, where one transaction funds an account and the next ones spend that ETH in the same block. I covered that in how to rescue tokens from a compromised wallet with a sweeper bot.
- Ordered. You control the sequence, which is how backruns and same-block snipes work.
- Private and revert-protected. Bundles aren't visible in the public mempool before inclusion, and failed bundles don't land on-chain, so you don't pay gas for a trade that didn't work.
How to send a bundle
The endpoints
Flashbots has two main endpoints on mainnet, and mixing them up is a common first mistake. Per the Flashbots guide on sending transactions and bundles:
rpc.flashbots.netis Flashbots Protect, a drop-in wallet RPC for regular users. It takes normaleth_sendRawTransactioncalls. It does not take bundles.relay.flashbots.netis for searchers and bots. It accepts bundles and private transactions through Flashbots-specific methods. It does not support regular Ethereum RPC methods, so you still need a normal node for nonces, gas estimates and receipts.
For testing, there's also a Sepolia relay at relay-sepolia.flashbots.net.
The signing key
Every request to the relay is signed with an ECDSA key that identifies you as a searcher. The signature goes in the X-Flashbots-Signature header as address:signature, where the signature is over the JSON body. This key doesn't hold funds and shouldn't be your trading key. Flashbots uses it to build a reputation for you over time, and higher-reputation searchers get better access when the network is congested. So reuse the same identity key instead of generating a new one every run.
The eth_sendBundle request
The JSON-RPC docs describe the payload. The core fields are:
txs: an array of signed raw transactions, in order.blockNumber: the hex block number this bundle is valid for.- Optional:
minTimestampandmaxTimestamp,revertingTxHashes(transactions that are allowed to revert without killing the bundle),replacementUuid(to replace or cancel a bundle later) andbuilders(a list of other registered builders that should also receive the bundle).
The same docs list limits of 100 transactions and 300,000 bytes per bundle.
A minimal request body looks like this:
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendBundle",
"params": [
{
"txs": ["0x02f8...", "0x02f8..."],
"blockNumber": "0x1499a2c"
}
]
}
If you're in JavaScript, the official ethers-provider-flashbots-bundle library handles signing and gives you simulate() and sendBundle() helpers. Note that its README says it's designed for an existing ethers.js v5 installation.
Target the next block, and resubmit every block
A bundle is valid for one block. If you target block N+1 and it isn't included there, it's gone. The library README is explicit that if your bundle should be valid until it's mined, you need to resubmit for every block, and the target should always be a future block, never the current one.
Resubmitting each block is also your chance to re-check the opportunity, update fees to the new base fee, and re-sign with the correct nonce.
mev_sendBundle and MEV-Share
Flashbots also has mev_sendBundle, a newer format that supports MEV-Share, where you can backrun user transactions that are only partially revealed through hints. Their MEV-Share searcher writeup explains the strategies. If you're just starting, eth_sendBundle is the simple option and is what most examples use.
Always simulate first
Before you send anything, simulate it with eth_callBundle (the library's simulate() method). The simulation runs your signed transactions in order on top of a given block and returns gas used, fees paid and coinbase payments for each transaction.
This catches the majority of problems before they cost you an opportunity. The Flashbots troubleshooting guide recommends simulating, logging the results, and keeping a record of every bundle and its signed transactions, because a bundle that fails leaves no trace on Etherscan to debug from.
Why your bundle isn't being included: the checklist
The Flashbots troubleshooting guide lists five reasons a bundle doesn't land, in the order you should check them.
1. A transaction in the bundle reverts
If any transaction reverts (and isn't listed in revertingTxHashes), the whole bundle is dropped. Common causes: wrong nonce, insufficient balance, a stale price that makes your slippage check fail, a deadline that's already passed, or a token transfer that the token contract blocks.
Fix: simulate against the block you're targeting and read the per-transaction results.
2. You're not paying enough for the blockspace
Your transactions must pay at least the base fee, or the block would be invalid. Beyond that, the builder compares what your bundle pays with what it would earn from other transactions using the same gas. The docs give a simple example: if your bundle pays a 1 gwei priority fee but the cheapest transaction in the block pays 2 gwei, the builder is better off leaving your bundle out.
What matters is the effective priority fee: everything the bundle pays the builder (priority fees plus any direct ETH transfers to the block's coinbase), divided by the gas it uses.
Fix: pay more, or use less gas for the same result. Gas optimization is a bid in disguise.
3. A competitor bid more for the same opportunity
Builders roughly simulate each bundle at the top of the block, sort by effective priority fee, and then add bundles one by one as long as they still work. If another bundle that touches the same state goes first, yours may revert, collide on a nonce, or pay less than it did in isolation. Then it's dropped.
The auction is blind, so you can't see competing bids in advance. You can look afterward: the library's getConflictingBundle() helper re-simulates the target block to find which bundle conflicted with yours and how much it paid.
Fix: compare your effective priority fee with the winner's. If you're consistently outbid, the opportunity may be too competitive. In that case, a less crowded strategy beats a higher bid. My take on that is in how to build a profitable MEV arbitrage bot.
4. Your bundle arrived too late
Your bundle has to reach the builder, get simulated, go into a block, and get through a relay to the validator, all before the slot. The troubleshooting guide notes each of those steps can take around 1 to 2 seconds. There's also a window where your node still thinks the chain is at block X while the rest of the network has already seen X+1, so you're targeting a block that's already been built.
Fix: cut your own processing time, use low-latency, well-synced nodes, and send your bundle as early as you can in the slot.
5. The validator isn't running mev-boost
mev-boost is opt-in. If the validator for a slot doesn't run it, or isn't connected to a relay your builder submits to, no bundle will land in that block. Research on the builder market, such as this 2024 study, found that the large majority of blocks since the Merge come through mev-boost, but not all of them.
Fix: nothing to fix. Resubmit for the next block.
Send to more than one builder
Flashbots is one builder among several. The builder market is concentrated in a handful of players, and their share changes over time. You can see current relay and builder shares on relayscan.io and slot-level data on mevboost.pics.
If you only send to one builder, you only get into blocks that builder wins. You can use the builders field to have Flashbots share your bundle with other registered builders, and many builders run their own bundle endpoints. Check each builder's documentation for its exact API before relying on it.
Get refunds when you overpay
Eligible bundles that land in blocks built by BuilderNet (the block-building network Flashbots operates together with partners) can earn refunds of part of what they paid, based on how much they contributed to the block's value. The rules are in the BuilderNet refunds documentation. The Flashbots RPC docs include flashbots_getFeeRefundsByRecipient and related methods so you can see what you've earned, and flashbots_setFeeRefundRecipient to send refunds to a different address. It's worth checking, especially if you bid aggressively.
The short version
- Use
relay.flashbots.netfor bundles, with a stable identity key. - Simulate with
eth_callBundleevery time. - Target the next block and resubmit every block.
- Check the five failure reasons in order: reverts, fee too low, outbid, too late, no mev-boost.
- Send to multiple builders.
If you'd rather not build all of this yourself, the MEVDAO Sniper Bot and Token Rescue Bot already handle bundle building, simulation and submission for sniping and wallet rescues. They're included in the MEVDAO subscription with a 3-day free trial.
For more guides like this, join the free MEV Secrets email list below. And if you want a custom MEV bot built, message me on Telegram.