Skip to main content

MEV Bundles API

API Preview - Not Yet Available

The MEV Bundles API is currently in development. These endpoints are not yet available. This documentation describes the planned API design for reference.

Want early access? Contact hello@axol.io to join the beta waitlist.

Submit and simulate MEV bundles through Flashbots and other relays. The MEV API will provide bundle management, simulation, and private transaction submission for atomic execution.

Key Features

  • Bundle submission - Submit to Flashbots and other relays
  • Bundle simulation - Test bundles before submission
  • Private transactions - Flashbots Protect for sandwich protection
  • Multi-relay support - Flashbots, MEV-Boost, BloxRoute, Eden
  • Status tracking - Monitor bundle inclusion

Supported Relays

RelayChainsNotes
FlashbotsethereumPrimary relay
MEV-BoostethereumBuilder network
BloxRouteethereumBDN network
EdenethereumPriority network
MerklegnosisGnosis chain

Planned Endpoints

info

All endpoints below are planned designs and not yet implemented.

Submit Bundle

POST /v1/mev/{chain}/bundle/submit

Submit a bundle of transactions for atomic inclusion.

Request

{
"transactions": [
{
"signed_tx": "0x02f8...",
"can_revert": false
},
{
"signed_tx": "0x02f8...",
"can_revert": true
}
],
"target_block": null,
"min_timestamp": null,
"max_timestamp": null,
"revert_on_fail": true
}

Parameters

FieldTypeDescription
transactionsarrayOrdered list of signed transactions
transactions[].signed_txstringSigned transaction hex
transactions[].can_revertbooleanWhether this tx can revert
target_blockintTarget block number (null for next)
min_timestampintMinimum block timestamp
max_timestampintMaximum block timestamp
revert_on_failbooleanCancel if any tx fails

Query Parameters

ParameterTypeDescription
relaystringTarget relay (flashbots, mev-boost, etc.)
simulatebooleanSimulate before submitting

Response

{
"bundle_hash": "0xabc...",
"relay": "flashbots",
"target_block": 19500001,
"transactions": [
{"hash": "0x123...", "index": 0},
{"hash": "0x456...", "index": 1}
],
"simulation": {
"success": true,
"coinbase_diff": "50000000000000000",
"gas_used": 250000,
"state_changes": [...]
},
"submitted_at": "2026-01-15T10:30:00Z"
}

Simulate Bundle

POST /v1/mev/{chain}/bundle/simulate

Simulate a bundle without submitting.

Request

{
"transactions": [
{"signed_tx": "0x02f8...", "can_revert": false}
],
"block_number": null,
"state_block_number": null
}

Response

{
"success": true,
"simulation_block": 19500000,
"results": [
{
"tx_hash": "0x123...",
"success": true,
"gas_used": 150000,
"gas_price": 25000000000,
"coinbase_transfer": "25000000000000000",
"logs": [...],
"state_changes": [
{
"address": "0xabc...",
"key": "0x...",
"before": "0x...",
"after": "0x..."
}
]
}
],
"total_gas_used": 150000,
"coinbase_diff": "25000000000000000",
"coinbase_diff_eth": 0.025
}

Get Bundle Status

GET /v1/mev/{chain}/bundle/{bundle_hash}

Check the status of a submitted bundle.

Response

{
"bundle_hash": "0xabc...",
"status": "included",
"target_block": 19500001,
"included_block": 19500001,
"transactions": [
{
"hash": "0x123...",
"status": "included",
"block_number": 19500001,
"gas_used": 150000
}
],
"relay": "flashbots",
"submitted_at": "2026-01-15T10:30:00Z",
"included_at": "2026-01-15T10:30:12Z"
}

Status Values

StatusDescription
pendingSubmitted, waiting for inclusion
includedSuccessfully included in block
failedBundle failed (reverted or not included)
expiredTarget block passed without inclusion
cancelledBundle was cancelled

Cancel Bundle

DELETE /v1/mev/{chain}/bundle/{bundle_hash}

Attempt to cancel a pending bundle.

{
"bundle_hash": "0xabc...",
"cancelled": true,
"message": "Bundle cancellation submitted"
}

Send Private Transaction

POST /v1/mev/{chain}/private

Submit a single transaction via Flashbots Protect (private mempool).

Request

{
"signed_tx": "0x02f8...",
"max_block_number": null,
"hints": ["calldata", "logs"]
}

Parameters

FieldTypeDescription
signed_txstringSigned transaction hex
max_block_numberintMax block for inclusion (null = 25 blocks)
hintsarrayData to share with builders (none, calldata, logs, all)

Response

{
"tx_hash": "0x123...",
"status": "pending",
"max_block": 19500025,
"submitted_at": "2026-01-15T10:30:00Z"
}

Code Examples

Python - Bundle Submission

import asyncio
import aiohttp
from web3 import Web3
from eth_account import Account

class MEVClient:
def __init__(self, api_key: str, private_key: str, chain: str = "ethereum"):
self.api_key = api_key
self.chain = chain
self.base_url = "https://api.axol.io/api/v1"
self.account = Account.from_key(private_key)
self.w3 = Web3()

async def simulate_bundle(self, signed_txs: list[str]) -> dict:
"""Simulate a bundle."""
async with aiohttp.ClientSession() as session:
url = f"{self.base_url}/mev/{self.chain}/bundle/simulate"
payload = {
"transactions": [
{"signed_tx": tx, "can_revert": False}
for tx in signed_txs
]
}

async with session.post(
url,
headers={"X-API-Key": self.api_key},
json=payload
) as resp:
return await resp.json()

async def submit_bundle(
self,
signed_txs: list[str],
target_block: int = None,
relay: str = "flashbots"
) -> dict:
"""Submit a bundle."""
async with aiohttp.ClientSession() as session:
url = f"{self.base_url}/mev/{self.chain}/bundle/submit"
payload = {
"transactions": [
{"signed_tx": tx, "can_revert": False}
for tx in signed_txs
],
"target_block": target_block,
"revert_on_fail": True
}

async with session.post(
url,
headers={"X-API-Key": self.api_key},
json=payload,
params={"relay": relay, "simulate": True}
) as resp:
return await resp.json()

async def get_bundle_status(self, bundle_hash: str) -> dict:
"""Check bundle status."""
async with aiohttp.ClientSession() as session:
url = f"{self.base_url}/mev/{self.chain}/bundle/{bundle_hash}"

async with session.get(
url,
headers={"X-API-Key": self.api_key}
) as resp:
return await resp.json()

async def send_private(self, signed_tx: str) -> dict:
"""Send via Flashbots Protect."""
async with aiohttp.ClientSession() as session:
url = f"{self.base_url}/mev/{self.chain}/private"
payload = {
"signed_tx": signed_tx,
"hints": ["calldata"]
}

async with session.post(
url,
headers={"X-API-Key": self.api_key},
json=payload
) as resp:
return await resp.json()

# Usage
async def main():
client = MEVClient(
api_key=os.getenv("AXOL_API_KEY"),
private_key=os.getenv("PRIVATE_KEY")
)

# Build transactions
tx1 = {...} # Your first transaction
tx2 = {...} # Your second transaction

signed_tx1 = client.account.sign_transaction(tx1).rawTransaction.hex()
signed_tx2 = client.account.sign_transaction(tx2).rawTransaction.hex()

# Simulate first
sim = await client.simulate_bundle([signed_tx1, signed_tx2])
print(f"Simulation success: {sim['success']}")
print(f"Expected profit: {sim['coinbase_diff_eth']} ETH")

if sim['success']:
# Submit bundle
result = await client.submit_bundle([signed_tx1, signed_tx2])
print(f"Bundle hash: {result['bundle_hash']}")

# Wait and check status
await asyncio.sleep(15)
status = await client.get_bundle_status(result['bundle_hash'])
print(f"Status: {status['status']}")

asyncio.run(main())

TypeScript - Arbitrage Bundle

import { ethers } from 'ethers';

interface BundleTransaction {
signed_tx: string;
can_revert: boolean;
}

interface SimulationResult {
success: boolean;
coinbase_diff_eth: number;
total_gas_used: number;
}

class ArbitrageBot {
private apiKey: string;
private wallet: ethers.Wallet;
private baseUrl: string;

constructor(apiKey: string, privateKey: string) {
this.apiKey = apiKey;
this.wallet = new ethers.Wallet(privateKey);
this.baseUrl = 'https://api.axol.io/api/v1';
}

async simulateArbitrage(
buyTx: ethers.TransactionRequest,
sellTx: ethers.TransactionRequest
): Promise<SimulationResult> {
const signedBuy = await this.wallet.signTransaction(buyTx);
const signedSell = await this.wallet.signTransaction(sellTx);

const response = await fetch(`${this.baseUrl}/mev/ethereum/bundle/simulate`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
transactions: [
{ signed_tx: signedBuy, can_revert: false },
{ signed_tx: signedSell, can_revert: false }
]
})
});

return response.json();
}

async executeArbitrage(
buyTx: ethers.TransactionRequest,
sellTx: ethers.TransactionRequest,
minProfitEth: number
): Promise<string | null> {
// Simulate first
const sim = await this.simulateArbitrage(buyTx, sellTx);

if (!sim.success) {
console.log('Simulation failed');
return null;
}

if (sim.coinbase_diff_eth < minProfitEth) {
console.log(`Profit ${sim.coinbase_diff_eth} ETH below minimum`);
return null;
}

// Sign and submit
const signedBuy = await this.wallet.signTransaction(buyTx);
const signedSell = await this.wallet.signTransaction(sellTx);

const response = await fetch(`${this.baseUrl}/mev/ethereum/bundle/submit`, {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
transactions: [
{ signed_tx: signedBuy, can_revert: false },
{ signed_tx: signedSell, can_revert: false }
],
revert_on_fail: true
}),
params: { relay: 'flashbots', simulate: 'true' }
});

const result = await response.json();
return result.bundle_hash;
}
}

Best Practices

1. Always Simulate First

Never submit without simulating:

sim = await client.simulate_bundle(txs)
if not sim['success']:
print(f"Simulation failed: {sim.get('error')}")
return

if sim['coinbase_diff_eth'] < MIN_PROFIT:
print("Not profitable")
return

# Now submit
result = await client.submit_bundle(txs)

2. Use Appropriate Relay

Different relays have different characteristics:

RelayBest For
FlashbotsStandard MEV, privacy
MEV-BoostMaximum builder coverage
BloxRouteLow latency
EdenPriority for stakers

3. Handle Competition

Other searchers are competing for the same opportunities:

# Include competitive priority fee
tx['maxPriorityFeePerGas'] = web3.to_wei(5, 'gwei')

# Or include coinbase transfer for higher priority
coinbase_payment = web3.to_wei(0.01, 'ether')

4. Track Bundle Status

Don't assume inclusion - verify:

for i in range(10):  # Check for 10 blocks
status = await client.get_bundle_status(bundle_hash)

if status['status'] == 'included':
print(f"Included in block {status['included_block']}")
break
elif status['status'] in ('failed', 'expired'):
print(f"Bundle failed: {status['status']}")
break

await asyncio.sleep(12) # One block

5. Use Private Transactions for Protection

For non-MEV transactions, use Flashbots Protect:

# Avoid sandwich attacks on your swap
result = await client.send_private(signed_swap_tx)

Rate Limits

TierSimulations/minSubmissions/min
Free105
Pro10050
Enterprise1000500

See Also