MetaTrader Python API: MT4 & MT5 from Python, on any OS
The official MetaTrader5 package needs Windows and a MetaTrader 5 app running next to your script. MetaTrader API is plain HTTPS and WebSocket: requests and websockets are all you need, on Linux, macOS, Docker or serverless.
import os, uuid, requests
API = "https://api.metatraderapi.net"
s = requests.Session()
s.headers["x-api-key"] = os.environ["MTAPI_KEY"]
ACCOUNT = os.environ["MTAPI_ID"]
def call(path, **params):
r = s.get(f"{API}/{path}", params={"id": ACCOUNT, **params}, timeout=75)
r.raise_for_status()
return r.json()
print(call("AccountSummary"))
order = call("OrderSend", symbol="EURUSD", operation="Buy", volume=0.01,
clientOrderId=f"py-{uuid.uuid4().hex[:20]}")
if order.get("state") != 10009:
raise RuntimeError(f"broker refused the order: {order.get('state')}")import asyncio, json, os, websockets # pip install "websockets>=14"
async def main():
url = f"wss://api.metatraderapi.net/v1/stream?id={os.environ['MTAPI_ID']}"
async with websockets.connect(url, additional_headers={"x-api-key": os.environ["MTAPI_KEY"]}) as ws:
await ws.send(json.dumps({"action": "subscribe", "symbol": "EURUSD"}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] == "QUOTE":
print(msg["data"]["symbol"], msg["data"]["bid"], msg["data"]["ask"])
asyncio.run(main())import os, pandas as pd, requests
API = "https://api.metatraderapi.net"
s = requests.Session()
s.headers["x-api-key"] = os.environ["MTAPI_KEY"]
# closed trades into a DataFrame for a trading journal
rows = s.get(f"{API}/OrderHistory", params={"id": os.environ["MTAPI_ID"]}, timeout=75).json()
df = pd.DataFrame(rows)
print(df[["ticket", "symbol", "profit"]].tail())What is the MetaTrader Python API?
There are two ways to use MetaTrader from Python. The official MetaTrader5 package from MetaQuotes connects to a MetaTrader 5 desktop app running on the same Windows machine; it does not support MT4. A hosted API such as MetaTrader API runs the account connection on its side and is called from Python with ordinary HTTP and WebSocket libraries, so the same script works on Linux, macOS or Windows, for MT4 and MT5 accounts, with many accounts per API key.
From account to first API call in minutes
- STEP 1Connect an MT4 or MT5 accountIn the dashboard: broker server, login, main password. Use a broker demo account while you build.
- STEP 2Export the key and account idexport MTAPI_KEY=mt5api_… and export MTAPI_ID=<account uuid>
- STEP 3pip install requests websocketsNo MetaTrader package, no Windows, no Wine. The same script runs on a laptop, in Docker or in a cloud function.
What the MetaTrader Python API gives you
A selection of the 100+ endpoints. All of them, with parameters and responses: documentation and OpenAPI.
| Area | Endpoint | What it does |
|---|---|---|
| Account | /AccountSummary | balance, equity, margin as JSON |
| Market data | /GetQuote | bid / ask; /GetQuoteMany for several symbols |
| Market data | /PriceHistory | candles ready for a pandas DataFrame |
| Orders | /OrderSend | market and pending orders with clientOrderId |
| Orders | /OrderClose | close fully or partially |
| Positions | /OpenedOrders | open positions and pending orders |
| History | /OrderHistory | closed trades for reporting and journals |
Ways to reach MetaTrader from Python, compared
THE USUAL WAYS
- Official MetaTrader5 package (pip install MetaTrader5): Windows only; needs a logged-in MetaTrader 5 desktop app on the same machine; no MT4.
- MT4 + ZeroMQ / file bridge: an Expert Advisor plus a DLL inside a running MetaTrader 4 app; fragile and Windows-bound.
- Wine on Linux: MetaTrader under a Windows compatibility layer: possible, but you maintain the app, updates and logins yourself.
METATRADER API
- MetaTrader API: plain HTTPS from Python on any OS, MT4 and MT5, one key for many accounts, live data over one WebSocket.
- Works with your own account at any broker: no broker permission, Manager licence or server access needed.
- Each account runs on its own isolated connection; broker passwords are stored AES-256-GCM encrypted.
- Uptime measured live on the public status page.
From the MetaTrader5 package to the API
The official package's calls and their equivalents here. Your logic stays; only the transport changes.
| MetaTrader5 package (Windows) | MetaTrader API (any OS) |
|---|---|
mt5.initialize() / mt5.login() | connect once in the dashboard or POST /v1/seats; then just send x-api-key + ?id= |
mt5.account_info() | GET /AccountSummary |
mt5.symbols_get() | GET /SymbolList |
mt5.symbol_info_tick(symbol) | GET /GetQuote?symbol=… (or the QUOTE frames on the WebSocket) |
mt5.copy_rates_from_pos(…) | GET /PriceHistory |
mt5.order_send(request) | GET /OrderSend with clientOrderId (state 10009 = done) |
mt5.positions_get() / mt5.orders_get() | GET /OpenedOrders |
mt5.history_deals_get(…) | GET /OrderHistory |
mt5.shutdown() | nothing to shut down: the connection stays on our side |
Writing safe Python for orders
Send every order with a unique clientOrderId: a resend with the same id returns the first result instead of a second order. Check state in the response (10009 = done), because HTTP 200 only means the broker answered. Use a 75 s client timeout, retry reads on 429/503 after Retry-After, and after a TIMEOUT on an order read /OpenedOrders before doing anything else. Stream prices over the WebSocket instead of polling to stay far below the rate limit of 20 requests per second per key.
Limits, in numbers
| Rate limit | bursts of 40 requests, refilled at 20 per second, per API key |
| WebSocket | 25 concurrent streams per account, quotes at most every 200 ms per symbol, heartbeat every 10 s |
| Timeouts | a REST call waits up to 30 s for the account (MT4: 15 s); set your client timeout to 75 s |
| Accounts | up to 50 seats per customer, more on request |
| Retries | every order can carry a clientOrderId: a resend with the same id never opens a second order |
One price per connected account
REST, WebSocket, gRPC, MT4 and MT5 included. Billed monthly; add or release seats anytime.