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​