# Trade MetaTrader 5 from Python with the fxapis SDK

> pip install fxapis: connect an MT5 account, place orders that never double-fill, and let the SDK retry only what is safe — from Python on Linux, macOS or Windows.

Published 2026-10-08 by fxapis. Canonical: https://fxapis.com/blog/mt5-api-python-sdk

## In short

- pip install fxapis gives you sync and async clients for the hosted MT5 API, fully typed, on Python 3.10 or newer — with no MetaTrader terminal on your machine.
- Orders get an idempotency key automatically, so a retried order returns the first answer instead of opening a second position.
- Each failure is its own exception. OrderUnresolvedError is never retried — wait_until_resolved() polls it; codes that prove nothing was done are retried for you.
- It runs anywhere Python runs, including Linux and macOS, unlike the official MetaTrader5 package.

The official Python SDK for fxapis is a typed client for the hosted MetaTrader 5 REST API: **sync and async clients on httpx, idempotency keys added for you, and one exception class per error code**. It needs Python 3.10 or newer and is open source at [github.com/FXapis/fxapis-python](https://github.com/FXapis/fxapis-python).

Unlike the official `MetaTrader5` package, it does not drive a terminal on your machine. The terminals run in fxapis's cloud; the SDK makes HTTPS requests — so it runs on Linux, macOS, Windows, a container or a scheduled job. [The MetaTrader5 package vs a REST API](https://fxapis.com/blog/metatrader5-python-package-vs-rest-api) compares the two in detail.

## Install it

```bash
pip install fxapis
export FXAPIS_API_KEY="fx_test_…"   # create one at fxapis.com/dashboard/keys
```

Use a broker **demo** account while you build: every fxapis key, `fx_test_` keys included, reaches a real broker. There is no simulated market.

## From an MT5 login to a closed trade

```python
from fxapis import Fxapis

client = Fxapis()  # reads FXAPIS_API_KEY

# 1. Connect the account once. The trading password, not the investor password.
account = client.accounts.connect(
    login="26177561",
    server="VantageMarkets-Demo",
    password="your-mt5-trading-password",
    mode="warm_on_demand",  # online when needed, offline after 15 idle minutes
)

# 2. Bring it online and wait for `ready` — about ten seconds for a typical broker.
client.accounts.warm(account["id"])
client.accounts.wait_until_ready(account["id"])

# 3. Trade. Volumes and prices are strings: "0.01", not 0.01.
order = client.orders.market(account["id"], symbol="EURUSD", side="buy", volume="0.01")
print(order["state"], order["filledPrice"])

# 4. Read the positions, then close them.
for position in client.positions.list(account["id"]):
    print(position["symbol"], position["volume"], position["profit"])
    client.positions.close(account["id"], position["brokerPositionId"])
```

`with Fxapis() as client:` closes the connection pool for you. Connecting does not log in: it stores the account and encrypts its password, and `warm` starts the login. `wait_until_ready()` raises if the account needs a person — a wrong password, 2FA, a certificate — instead of waiting forever.

## Orders that never double-fill

`client.orders.market()` sends an `Idempotency-Key` on every order, generated for you. If the connection drops after the order was sent, the SDK resends it with the **same** key, and fxapis answers with the first result instead of opening a second position. When the key should mean something in your system — a signal and a member, a copied deal — pass your own:

```python
key = f"signal_{signal_id}:member_{member_id}"
order = client.orders.market(account_id, symbol="XAUUSD", side="buy", volume="0.05", idempotency_key=key)
```

A member who taps twice, or a job that runs twice, gets the same order back. [Idempotent orders](https://docs.fxapis.com/guides/idempotency) explains why this matters.

## Handling each outcome

```python
from fxapis import OrderRejectedError, OrderUnresolvedError, SendFailedError, AccountNotReadyError

try:
    order = client.orders.market(account_id, symbol="XAUUSD", side="buy", volume="0.05", idempotency_key=key)
except OrderRejectedError as err:
    print("rejected:", err.message)        # nothing opened; a new attempt needs a NEW key
except OrderUnresolvedError as err:
    order = client.orders.wait_until_resolved(err.order_id)   # never resend: wait for the answer
except (SendFailedError, AccountNotReadyError) as err:
    print("not sent:", err.code)           # already retried with the same key; safe to try later
```

The SDK retries only what is provably safe: codes that mean nothing was done are resent with the same key, and server errors and lost connections are retried on reads and on requests carrying a key. `ORDER_UNRESOLVED` is never retried — the order may be live at the broker — and fxapis confirms an opening order against the broker's own deal history.

## Async, pages and many accounts

```python
import asyncio
from fxapis import AsyncFxapis

async def main() -> None:
    async with AsyncFxapis() as client:
        async for order in client.orders.iter(state="unknown"):
            print(order["id"], order["symbol"])

asyncio.run(main())
```

`iter()` walks a paginated list for you. For one trade on many accounts — a copier, a signal service — `client.multi_account_orders.create()` sends a single order to up to 1,000 accounts, within your plan's limit, with a volume per account.

## Where to go next

- [fxapis-examples](https://github.com/FXapis/fxapis-examples) has a quickstart, click-to-trade and a trade copier in Python — see [the examples post](https://fxapis.com/blog/mt5-api-examples-python-node-curl).
- [SDKs and examples](https://docs.fxapis.com/sdks) in the docs, and the [Python guide](https://docs.fxapis.com/guides/python) to see every request with plain `requests`.
- [Trading](https://docs.fxapis.com/trading) covers every outcome an order can have, and [Errors](https://docs.fxapis.com/errors) every code.

## Questions

### How is this different from the MetaTrader5 Python package?

The MetaTrader5 package drives a terminal installed on the same Windows machine. The fxapis SDK calls a hosted REST API instead: the terminal runs in fxapis's cloud, so your code runs on any OS and nothing has to stay logged in on your side.

### Which Python versions are supported?

Python 3.10 or newer. The package is fully typed (py.typed) and checked with mypy --strict; httpx is its only dependency.

### Does it retry orders automatically?

Only when it is provably safe: codes that mean nothing was done (SEND_FAILED, ACCOUNT_NOT_READY, RATE_LIMITED and similar) are retried with the same idempotency key. ORDER_UNRESOLVED and broker rejections are never retried.

### Is there an async client?

Yes — AsyncFxapis has the same methods, awaited, and async iterators for paginated lists.
