A trade copier is the one place in an MT5 integration where a wrong config
costs real money on an account you might not even be watching. Set the lot
mode to multiplier when you meant fixed and a 2-lot master trade becomes
a 2-lot follower trade on a $1,000 account. The copier did exactly what it
was told.
The conceptual post explains the pipeline. This one is the how-to: create the copier over the REST API, size it correctly, map symbols, guard slippage, and confirm the first fill before you walk away.
It's also the way to make trades happen on several accounts at once without writing execution code. If you'd rather place orders yourself, the order endpoints do that directly. A copier is for the other shape: you trade one source account, by hand or through the API, and every follower gets the same trades without a line of code per account.
Prerequisites
Two connected accounts, and the follower has to be a full account. A
read-only account is logged in with the investor password, which can't place
orders, so it can't follow. Try it and you get a 400 invalid_request with
"Follower must be a full account". The source can be either tier; watching
with an investor password is fine.
Both accounts must be connected when you create the copier, and you need an
API key with the full scope (a readonly key can't POST).
export METAKIT_KEY="stk_live_..."
curl -s https://api.metakit.cloud/v1/accounts -H "Authorization: Bearer $METAKIT_KEY" \
| python -c "import sys,json; [print(a['id'], a['type'], a['status'], a['currency'], a['balance']) for a in json.load(sys.stdin)['data']]"Note the ids, types, and balances. You'll want the balances for the sizing maths in a moment.
Create the trade copier over the API
curl -s -X POST https://api.metakit.cloud/v1/copiers \
-H "Authorization: Bearer $METAKIT_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_account_id": 3,
"follower_account_id": 5,
"state": "off",
"lot_mode": "multiplier",
"lot_value": 0.5,
"reverse": false,
"copy_existing": false,
"filters": { "symbols": ["EURUSD", "XAUUSD"], "max_open_positions": 10 }
}'Two deliberate choices there. "state": "off" creates it paused so you can
finish configuring before anything replicates; you flip it to on at the
end. And copy_existing: false means positions already open on the source
are ignored. Set it to true and the copier opens follower copies of the
source's current open positions on creation, at whatever price is available
now, which is rarely what you want on a live account.
The response is the copier object with an id, and every field echoed back
in snake_case.
Pick a sizing mode, with the numbers
lot_mode decides how a source volume becomes a follower volume. Assume the
source has a $10,000 balance, the follower has $2,500, and the master opens
0.80 lots of EURUSD.
fixed: the follower always trades lot_value lots, regardless of what
the master did. With lot_value: 0.10, the 0.80 becomes 0.10. So does a
0.01 scalp and a 5.00 punt. Use it when follower risk has to be constant and
the master's sizing is not to be trusted.
multiplier: source volume times lot_value. With lot_value: 0.5,
0.80 becomes 0.40. Simple and predictable, and it stays correct only as long
as the two balances keep the same ratio; if the follower draws down 30% the
multiplier doesn't know.
proportional: source volume scaled by follower balance divided by source
balance, read live at copy time. Here that's 2,500 / 10,000 = 0.25, so 0.80
becomes 0.20. The lot value isn't part of this formula. This is the balance-
ratio mode, and it's the right default for "equal relative risk" across
followers of different sizes.
Whatever the mode produces still has to be a valid lot at the follower's
broker. 0.03 lots on the master under proportional at 0.25 gives 0.0075,
which is below any broker's minimum. Set filters.min_lot (say 0.01) and
copies that scale below it are skipped with a copier.trade_skipped event
rather than rounded up into more risk than you asked for. On the other side,
filters.max_open_positions stops the follower stacking more positions than
you want, and filters.max_lot caps the volume after scaling so one
oversized master trade can't wreck the follower.
You can change all of this later without recreating the copier:
curl -s -X PUT https://api.metakit.cloud/v1/copiers/7/risk \
-H "Authorization: Bearer $METAKIT_KEY" \
-H "Content-Type: application/json" \
-d '{ "lot_mode": "proportional", "lot_value": 1, "filters": { "min_lot": 0.01, "max_lot": 2, "max_open_positions": 10 } }'Map symbols with the bulk endpoint
Brokers name the same instrument differently. Gold is XAUUSD at one and
GOLD at another; the follower's broker may suffix everything with .pro or
.r. The copier matches names exactly, so a source XAUUSD trade with no
GOLD at the follower is skipped, not guessed. You tell it the mapping:
curl -s -X PUT https://api.metakit.cloud/v1/copiers/7/symbol-map \
-H "Authorization: Bearer $METAKIT_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "merge",
"map": { "XAUUSD": "GOLD", "EURUSD": "EURUSD.pro", "US30": "US30.cash" }
}'Keys are source symbols, values are follower symbols. mode: "merge"
upserts into the existing map; sending an empty string as a value removes
that key. mode: "replace" swaps the whole map for what you sent. The map
also works at creation as symbol_map in the POST body, but the bulk
endpoint is what you want once the copier exists.
Check the follower's actual symbol names first rather than assuming:
GET /v1/accounts/5/symbols lists them, and
GET /v1/accounts/5/symbols/GOLD returns the full spec including min_lot
and lot_step, which is also where you find out that gold's contract size
is 100 and not the 100,000 you'd assume from FX. The
symbol suffixes post
is about exactly this.
Slippage guards and SL/TP
Between the master's fill and the follower's fill, price moves. The follower
pays that difference on top of whatever the master already paid. MT5 has no
pre-trade slippage tolerance on market orders, so the guard works after the
fact: set a maximum slippage in pips on the copier, and a copy that fills
further from the master's price than that is closed straight back out and
reported as a copier.trade_skipped with ok: false. You eat the spread on
a round trip instead of holding a position at a price the master never had.
One honest note: the slippage guard and the SL/TP toggle below live in the
copier's settings in the dashboard for now, not on the /v1 risk endpoint,
which only takes lot mode, lot value and filters.
Positive slippage in the copier's reporting always means worse for the follower, in both directions. Don't set the tolerance to zero; even a good broker will fill a pip away at the London open.
The SL/TP toggle controls whether the master's stop loss and take profit are mirrored onto the copy. It defaults to on and you almost always want it. The exception is a follower running its own hard risk rules, for example a funded account with a daily loss cap, where you'd rather an equity monitor manage the downside.
Turn it on and verify the first fill
curl -s -X PATCH https://api.metakit.cloud/v1/copiers/7 \
-H "Authorization: Bearer $METAKIT_KEY" \
-H "Content-Type: application/json" \
-d '{ "state": "on" }'Now open a small trade on the source and watch. The copier emits a webhook
for every decision, and the one you're waiting for is copier.trade_copied:
{
"event": "copier.trade_copied",
"timestamp": "2026-08-16T09:41:00.000Z",
"copier_id": 7,
"source_account_id": 3,
"follower_account_id": 5,
"action": "open",
"message": "Copied EURUSD buy 0.40",
"ok": true,
"latency": { "a": 120, "b": 45, "c": 30, "total": 195 }
}latency is in milliseconds: a is detection, b dispatch, c execution
at the follower's broker. Master fill to follower fill is consistently under
a second. If instead you get copier.trade_skipped, the message says why:
not in the symbol filter, below min_lot, max open positions reached, or
slippage exceeded. copier.error means the follower rejected the order
(insufficient margin, market closed, terminal down). copier.orphan means
the master closed something the follower never had a copy of, which is the
one that tells you the two accounts have drifted apart.
A short receiver that watches one copier, assuming you've already got signature verification in place:
type CopierEvent = {
event: "copier.trade_copied" | "copier.trade_skipped" | "copier.error" | "copier.orphan";
copier_id: number;
action: string;
message: string;
ok: boolean;
latency?: { a: number; b: number; c: number; total: number };
};
export function onCopierEvent(e: CopierEvent) {
if (e.copier_id !== 7) return; // every webhook you own gets every event
const tag = e.ok ? "ok " : "FAIL";
const ms = e.latency ? `${e.latency.total}ms` : "";
console.log(`[${tag}] ${e.event} ${e.action} ${e.message} ${ms}`);
if (e.event === "copier.orphan" || e.event === "copier.error") {
// page someone; the accounts are no longer mirrors of each other
}
}After a few trades, GET /v1/copiers/7/performance?range=1m gives you
fidelity_pct, latency percentiles, slippage_by_symbol, and copied_pnl
attributed by follower ticket, so the follower's own manual trades don't
leak into the number.
Pause with monitor, not off
Three states, and the middle one is the useful one.
on replicates everything: opens, closes, SL/TP changes, pending orders.
off stops all of it, including closes, so if the master exits a position
while the copier is off, the follower keeps holding it. monitor opens
nothing new but still mirrors closes, which winds the link down cleanly: the
follower's copied positions exit when the master's do, and nothing fresh
appears.
curl -s -X PATCH https://api.metakit.cloud/v1/copiers/7 \
-H "Authorization: Bearer $METAKIT_KEY" \
-H "Content-Type: application/json" \
-d '{ "state": "monitor" }'When you want to stop copying a strategy, monitor is almost always what
you mean.
Deleting a copier leaves the positions open
DELETE /v1/copiers/7 returns 204 and removes the link. It does not touch
the follower account. Every position the copier opened is still open, now
with nothing watching the master to close it. People find this out on a
Monday.
The safe sequence is monitor, wait until the follower's copied positions
have all closed with the master's (or close them by hand), then delete. If
you need the follower flat now, close its positions on the follower's own
terminal or app first, then delete.
The copier fields above are the whole set; they're listed under "Copiers" in
llms.txt and the
docs. Start with state: "off", one symbol,
and a 0.01-lot test trade on a demo follower. The fastest way to learn what
multiplier does is not on a funded account.