DEV Community

Patrick DeVos
Patrick DeVos

Posted on

How to Build an EVE Online Market Trading Bot in Python

EVE Online has one of the most complex player-driven economies in gaming. Billions of ISK change hands every day through player-controlled markets across hundreds of star systems. If you've ever wanted to write a trading bot that finds profitable routes — this guide walks you through it using real, live market data.

We'll use the EVE Online Market Tool API on RapidAPI, which wraps the EVE ESI (EVE Swagger Interface) and does the heavy lifting of scanning thousands of market orders so you don't have to.

What We're Building

A Python script that:

  1. Fetches current trading opportunities from the EVE market
  2. Finds arbitrage routes between stations
  3. Scores each opportunity by profit potential and volume
  4. Prints a ranked list of trades to execute

Prerequisites

  • Python 3.8+
  • A free RapidAPI account (the API has a free tier)
  • Basic Python knowledge

Step 1: Get Your API Key

  1. Go to RapidAPI and create a free account
  2. Search for "EVE Online Market Tool"
  3. Subscribe to the free BASIC plan
  4. Copy your X-RapidAPI-Key from the API console

Step 2: Install Dependencies

pip install requests tabulate colorama
Enter fullscreen mode Exit fullscreen mode

Step 3: The Trading Bot

import requests
import json
from tabulate import tabulate
from colorama import Fore, Style, init

init(autoreset=True)

RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY_HERE"
RAPIDAPI_HOST = "eve-online-market-tool.p.rapidapi.com"

BASE_URL = f"https://{RAPIDAPI_HOST}"

HEADERS = {
    "X-RapidAPI-Key": RAPIDAPI_KEY,
    "X-RapidAPI-Host": RAPIDAPI_HOST
}


def get_trading_opportunities():
    """Fetch station trading opportunities (buy low, sell high same station)."""
    response = requests.get(f"{BASE_URL}/scan/trading", headers=HEADERS)
    response.raise_for_status()
    return response.json()


def get_arbitrage_routes():
    """Fetch cross-region arbitrage routes (buy in one region, sell in another)."""
    response = requests.get(f"{BASE_URL}/scan/arbitrage", headers=HEADERS)
    response.raise_for_status()
    return response.json()


def get_npc_arbitrage():
    """Fetch NPC seeded items with player market arbitrage potential."""
    response = requests.get(f"{BASE_URL}/scan/npc-arbitrage", headers=HEADERS)
    response.raise_for_status()
    return response.json()


def analyze_item(item_name):
    """Get deep analysis for a specific item."""
    response = requests.get(
        f"{BASE_URL}/analyze/{item_name}",
        headers=HEADERS
    )
    response.raise_for_status()
    return response.json()


def format_isk(value):
    """Format ISK values with M/B suffixes."""
    if value >= 1_000_000_000:
        return f"{value/1_000_000_000:.1f}B"
    elif value >= 1_000_000:
        return f"{value/1_000_000:.1f}M"
    elif value >= 1_000:
        return f"{value/1_000:.1f}K"
    return f"{value:.0f}"


def display_trading_opportunities(data, top_n=10):
    """Display top station trading opportunities."""
    print(f"\n{Fore.CYAN}=== STATION TRADING OPPORTUNITIES ==={Style.RESET_ALL}")
    print("Buy and sell at the same station for guaranteed margin\n")

    if not data or "opportunities" not in data:
        print("No data available")
        return

    opportunities = sorted(
        data["opportunities"],
        key=lambda x: x.get("profit_margin_pct", 0),
        reverse=True
    )[:top_n]

    rows = []
    for opp in opportunities:
        margin = opp.get("profit_margin_pct", 0)
        color = Fore.GREEN if margin > 20 else Fore.YELLOW if margin > 10 else Fore.WHITE

        rows.append([
            color + opp.get("item_name", "Unknown") + Style.RESET_ALL,
            format_isk(opp.get("buy_price", 0)),
            format_isk(opp.get("sell_price", 0)),
            f"{margin:.1f}%",
            format_isk(opp.get("profit_per_unit", 0)),
            opp.get("volume_available", 0),
            opp.get("station_name", "Unknown")[:30]
        ])

    headers = ["Item", "Buy", "Sell", "Margin", "Profit/Unit", "Volume", "Station"]
    print(tabulate(rows, headers=headers, tablefmt="rounded_outline"))


def display_arbitrage_routes(data, top_n=10):
    """Display top arbitrage routes."""
    print(f"\n{Fore.CYAN}=== ARBITRAGE ROUTES ==={Style.RESET_ALL}")
    print("Buy in one region, haul to another for profit\n")

    if not data or "routes" not in data:
        print("No data available")
        return

    routes = sorted(
        data["routes"],
        key=lambda x: x.get("profit_total", 0),
        reverse=True
    )[:top_n]

    rows = []
    for route in routes:
        profit = route.get("profit_total", 0)
        color = Fore.GREEN if profit > 50_000_000 else Fore.YELLOW if profit > 10_000_000 else Fore.WHITE

        rows.append([
            color + route.get("item_name", "Unknown") + Style.RESET_ALL,
            route.get("buy_region", "?")[:20],
            route.get("sell_region", "?")[:20],
            format_isk(route.get("buy_price", 0)),
            format_isk(route.get("sell_price", 0)),
            format_isk(profit),
            route.get("quantity", 0)
        ])

    headers = ["Item", "Buy Region", "Sell Region", "Buy Price", "Sell Price", "Total Profit", "Qty"]
    print(tabulate(rows, headers=headers, tablefmt="rounded_outline"))


def main():
    print(f"{Fore.MAGENTA}╔══════════════════════════════════╗")
    print(f"║   EVE ONLINE MARKET TRADING BOT  ║")
    print(f"╚══════════════════════════════════╝{Style.RESET_ALL}")
    print("\nScanning markets...\n")

    try:
        # Fetch all data
        trading_data = get_trading_opportunities()
        arbitrage_data = get_arbitrage_routes()

        # Display results
        display_trading_opportunities(trading_data)
        display_arbitrage_routes(arbitrage_data)

        # Quick item analysis example
        print(f"\n{Fore.CYAN}=== ITEM DEEP DIVE: Tritanium ==={Style.RESET_ALL}")
        trit_data = analyze_item("Tritanium")
        if trit_data:
            print(json.dumps(trit_data, indent=2))

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print(f"{Fore.RED}Invalid API key. Check your RapidAPI key.{Style.RESET_ALL}")
        elif e.response.status_code == 429:
            print(f"{Fore.RED}Rate limit hit. Upgrade to PRO for higher limits.{Style.RESET_ALL}")
        else:
            print(f"{Fore.RED}API Error: {e}{Style.RESET_ALL}")


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Sample Output

╔══════════════════════════════════╗
║   EVE ONLINE MARKET TRADING BOT  ║
╚══════════════════════════════════╝

Scanning markets...

=== STATION TRADING OPPORTUNITIES ===
Buy and sell at the same station for guaranteed margin

╭─────────────────────┬──────────┬──────────┬────────┬────────────┬─────────┬────────────────────────────────╮
│ Item                │ Buy      │ Sell     │ Margin │ Profit/Unit│ Volume  │ Station                        │
├─────────────────────┼──────────┼──────────┼────────┼────────────┼─────────┼────────────────────────────────┤
│ PLEX                │ 4.1M     │ 5.3M     │ 29.3%  │ 1.2M       │ 847     │ Jita IV - Moon 4 - Caldari N..│
│ Skill Injector      │ 812M     │ 960M     │ 18.2%  │ 148M       │ 12      │ Jita IV - Moon 4 - Caldari N..│
│ Brutix Navy Issue   │ 218M     │ 255M     │ 17.0%  │ 37M        │ 3       │ Amarr VIII - Oris - Emperor F.│
╰─────────────────────┴──────────┴──────────┴────────┴────────────┴─────────┴────────────────────────────────╯
Enter fullscreen mode Exit fullscreen mode

Strategy Notes

Station Trading (Best for Beginners)

  • Works in Jita (the main trade hub) and Amarr
  • Set buy orders slightly above lowest buyer, sell orders slightly below lowest seller
  • Target items with 15%+ margins and high daily volume
  • Ideal starting capital: 500M ISK+

Arbitrage (Intermediate)

  • Buy underpriced items in low-traffic regions, haul to Jita
  • Risk: price can move during haul time (minimize with fast ships)
  • Caldari Shuttle for scanning, Iteron for hauling
  • Use the /scan/npc-arbitrage endpoint for NPC station seeded items — these replenish automatically

Risk Management

  • Never invest more than 20% of capital in a single trade
  • Check the /analyze/{item} endpoint for price history trends before big positions
  • Avoid items with fewer than 5 sellers (illiquid, spread can disappear)

Going Further

The API also supports:

# Get NPC arbitrage (guaranteed replenish sources)
npc_data = get_npc_arbitrage()

# Analyze specific items for trend data  
analysis = analyze_item("Caldari Navy Raven")
Enter fullscreen mode Exit fullscreen mode

PRO tier unlocks higher rate limits (1000 req/month vs 100 on free) — useful if you're running this on a schedule via cron.

Automating the Bot

Run it every hour with cron (Linux/Mac):

# crontab -e
0 * * * * /usr/bin/python3 /path/to/eve_bot.py >> /var/log/eve_bot.log 2>&1
Enter fullscreen mode Exit fullscreen mode

Or Windows Task Scheduler for a daily market report to your inbox.


The full API is available on RapidAPI — search "EVE Online Market Tool" by Circle of Wizards. Free tier includes 100 requests/month, plenty for personal use or small bots.

Happy trading, Capsuleer. o7

Top comments (0)