MetaTrader 4 APIErrors & Rate Limits
For AI agents: this page is also available as Markdown at /docs/mt4/errors.md. The index of every docs page is /docs/llms.txt.
Errors & Rate Limits
Every error from the MetaTrader 4 API is machine-readable. Auth failures return a fixed code, and everything else (timeouts, validation, broker or MQL rejections) comes back in a uniform envelope you can branch on.
Errors
A wrong or missing API key returns HTTP 401 with UNAUTHORIZED (see Authentication). A read-only fxs_ro_ key returns HTTP 403 with READ_ONLY_KEY on the trading routes and on any method other than GET or HEAD. An unknown account ID or path returns a plain 404 from the edge. Everything else (timeouts, validation failures, broker/MQL rejections) comes back in a uniform envelope with an error code, a message, and a command_id for log correlation:
{
"error": "MRPC_TIMEOUT",
"message": "MarketInfo.PriceHistory timed out",
"command_id": 19
}5xx responses come in two flavors. A 500 or 504 with the envelope above means the terminal processed your request but the underlying operation failed or timed out, for example {"error": "MRPC", "message": "CopyRates failed"} when requesting history for a symbol your broker doesn't list (check /symbols for the exact spelling, suffixes included). Envelope-less 5xx responses mean the terminal itself is down or restarting, for example during the brief restart after an API-key rotation. Trading routes can briefly return 503 right after a terminal start, before the trade engine has registered.
MT4 Trade Errors and Their Retcodes
A rejected order is not an error response: the request worked, the broker said no. It comes back as HTTP 200 with success: false. MT4 itself only reports an error number for that, so the bridge translates it into the matching MT5 retcode and puts the original number in retcodeDescription. Your code can branch on the same retcodes for MT4 and MT5 accounts, and the MT4 number is still there when you need it:
{
"success": false,
"outcome": "rejected",
"retcode": 10019,
"retcodeDescription": "MT4 error 134: Not enough money",
"deal": 0,
"order": 0,
...
}| MT4 error | Meaning | retcode |
|---|---|---|
| 1 | No result: the order already has these values | 10025 No changes |
| 128 | Trade timeout | 10031 No connection |
| 129 | Invalid price | 10015 Invalid price |
| 130 | Invalid stops | 10016 Invalid stops |
| 131 | Invalid trade volume | 10014 Invalid volume |
| 132 | Market is closed | 10018 Market closed |
| 133 | Trade is disabled | 10017 Trading disabled |
| 134 | Not enough money | 10019 Not enough money |
| 135 | Price changed | 10015 Invalid price |
| 136 | Off quotes | 10021 No quotes |
| 138 | Requote | 10004 Requote |
| 139 | Order is locked | 10013 Invalid request |
| 145 | Modification denied, too close to market | 10016 Invalid stops |
| 146 | Trade context is busy | 10024 Too many requests |
| 148 | Too many open and pending orders | 10033 Order limit reached |
| 4108 | Invalid ticket | 10013 Invalid request |
| 4109 | Trading not allowed | 10017 Trading disabled |
| 4110 / 4111 | Long or short positions not allowed | 10017 Trading disabled |
| any other | Anything not listed above | 10006 Rejected |
A successful market order reports 10009 (Done), a placed pending order 10008 (Placed). outcome sums the result up as applied, no_change (retcode 10025, safe to treat as applied), partial or rejected.
Requests MT4 cannot express at all never reach the broker. A stop-limit order (BuyStopLimit, SellStopLimit) is answered with HTTP 400 and {"error": "MRPC_VALIDATION", "message": "stop-limit orders are not supported on MT4"}. Error 146, trade context busy, means MT4 was still handling another order; MT4 processes one at a time, so retry shortly after.
Rate Limits
Terminal requests are served by your own dedicated terminal, so heavy polling only slows down your own account, nobody else's. Platform routes under /v1 are rate limited per API key: if you exceed a limit you get a 429 with a Retry-After header, so wait that long and retry. For live prices, positions, or account changes, prefer the WebSocket streams over polling: you get pushed updates without burning round-trips.