Multi-account execution
Copy trading, prop-desk fan-out and portfolio hedging all come down to one thing: doing the same trade on many accounts without writing a loop that half-fails. These two endpoints take a batch, dispatch every account concurrently to its own terminal, and answer with a per-account result. MT4 and MT5 can sit in the same batch.
Send one order to many accounts, or close across many accounts by selector, in a single call. Both endpoints fan out concurrently to each account's own terminal, work across MT4 and MT5 in the same batch, and need the full API key: a read-only key gets a 403. They are rate limited separately from the rest of the API at 60 requests/min.
/v1/ordersFan a single request out to several of your accounts at once, each leg with its own symbol, side, lot size and stops. Every leg inherits defaults and may override any of it, including with a falsy value, so slippage: 0 means zero rather than fall back. At most 20 legs per batch. Fields are snake_case; an unknown field is a 400 rather than a silent drop, because a quietly ignored stopLoss would place a naked position.
defaults: Values every leg inherits: symbol, operation, volume, price, slippage, stop_loss, take_profit, stop_limit_price, expiration, comment, expert_id.orders*: One entry per order. account_id is required; every other field either comes from defaults or is set here.orders[].account_id*: One of your accounts, as returned by GET /v1/accounts. The same account may appear more than once.orders[].operation: buy, sell, buyLimit, sellLimit, buyStop, sellStop, buyStopLimit or sellStopLimit (case-insensitive).orders[].volume: Lots. Must be greater than 0.Idempotency-Key: Header, up to 128 characters. Replaying the identical body with the same key returns the original response and sends nothing. Keys live 15 minutes.
{
"defaults": { "operation": "buy", "symbol": "EURUSD", "slippage": 20 },
"orders": [
{ "account_id": "6fcb1e23-8ea8-4674-aa62-3d48fd08f309", "volume": 0.10 },
{ "account_id": "a91b0d47-2c31-4f8a-9d76-1e5b0c3a7f42", "volume": 0.25,
"symbol": "EURUSD.sd" },
{ "account_id": "c73d55e2-90b4-4a17-8f3c-6d2e9a1b4c85", "volume": 0.01,
"symbol": "XAUUSD", "operation": "sell", "stop_loss": 2410.0 }
]
}{
"batch_id": "b_01J8Z4M7Q2K3V9",
"idempotent_replay": false,
"summary": { "requested": 3, "filled": 1, "failed": 2, "unknown": 0 },
"results": [
{
"account_id": "6fcb1e23-8ea8-4674-aa62-3d48fd08f309",
"platform": "mt5",
"symbol": "EURUSD", "operation": "buy", "volume": 0.10,
"status": "filled",
"order": 1234567, "deal": 987654,
"price": 1.08423, "bid": 1.08421, "ask": 1.08423,
"retcode": 10009, "retcode_description": "TRADE_RETCODE_DONE",
"message": "", "latency_ms": 812
},
{
"account_id": "a91b0d47-2c31-4f8a-9d76-1e5b0c3a7f42",
"platform": "mt5",
"symbol": "EURUSD.sd", "operation": "buy", "volume": 0.25,
"status": "rejected",
"order": 0, "deal": 0,
"retcode": 10019, "retcode_description": "TRADE_RETCODE_NO_MONEY",
"message": "not enough money", "latency_ms": 640
},
{
"account_id": "c73d55e2-90b4-4a17-8f3c-6d2e9a1b4c85",
"platform": "mt4",
"symbol": "XAUUSD", "operation": "sell", "volume": 0.01,
"status": "unavailable",
"message": "trade EA not ready — cannot execute trades",
"latency_ms": 55
}
]
}/v1/orders/closeClose across several accounts by intent rather than by ticket. Tickets are per account, you do not have them, and fetching them first means a second round trip against a list that goes stale when a stop fires. So this takes a selector and resolves the tickets itself: one lookup per account, then one close per match. At most 20 accounts, and 20 orders per account; matches past the cap come back skipped rather than being quietly dropped.
defaults: Selector values every account inherits: symbol, symbol_match, side, kind, magic, volume, slippage.accounts*: One entry per account. Each is a selector, or an explicit tickets list, never both on the same account.symbol*: Required. The symbol to close, as your broker names it. * closes every symbol and must be typed literally: an omitted symbol is a 400, never a silent close-everything.symbol_match: exact (default) matches as given. base also accepts a broker suffix, so EURUSD reaches EURUSD.sd and EURUSDm. A suffix is separator-led (.sd) or at most two alphanumerics (m), so EUR never matches EURUSD.kind: position (default) closes open positions only, pending deletes pending orders only, any does both. The default keeps a routine close from silently deleting resting orders.side: long, short, or any (default).volume: Partial close volume, applied to each matched position and subject to the symbol's volume step. Omit for a full close.tickets: Close exactly these tickets on this account, skipping the lookup. Tickets go stale, so prefer a selector unless you read them moments ago.require_reachable: Reject the whole batch up front if any account has no terminal, instead of reporting that account as unreachable.
{
"defaults": { "symbol": "EURUSD", "side": "long" },
"accounts": [
{ "account_id": "6fcb1e23-8ea8-4674-aa62-3d48fd08f309" },
{ "account_id": "a91b0d47-2c31-4f8a-9d76-1e5b0c3a7f42",
"symbol": "EURUSD.sd" },
{ "account_id": "c73d55e2-90b4-4a17-8f3c-6d2e9a1b4c85",
"tickets": [123456, 123457] }
]
}{
"batch_id": "b_01J8Z5N2R7T4W1",
"idempotent_replay": false,
"summary": {
"accounts": 3, "matched": 4, "closed": 3,
"failed": 1, "unknown": 0, "accounts_unknown": 0
},
"results": [
{
"account_id": "6fcb1e23-8ea8-4674-aa62-3d48fd08f309",
"platform": "mt5", "status": "closed",
"matched": 2, "closed": 2, "message": "", "latency_ms": 940,
"results": [
{ "ticket": 1234567, "symbol": "EURUSD", "type": "Buy",
"kind": "position", "volume": 0.10, "status": "closed",
"retcode": 10009, "retcode_description": "TRADE_RETCODE_DONE",
"price": 1.08430 }
]
},
{
"account_id": "c73d55e2-90b4-4a17-8f3c-6d2e9a1b4c85",
"platform": "mt4", "status": "nothing_matched",
"matched": 0, "closed": 0, "message": "", "latency_ms": 210,
"results": []
}
]
}Symbol suffixes are per broker: EURUSD, EURUSD.sd and EURUSDm are all live in the fleet, so one defaults.symbol across mixed brokers will partly fail by design. Override symbol per leg to fix it, or on a close use symbol_match: "base" to let one selector reach all three spellings.