cTrader APIErrors & rate limits
For AI agents: this page is also available as Markdown at /docs/ctrader/errors.md. The index of every docs page is /docs/llms.txt.
Errors & rate limits
Every error from the cTrader API is machine-readable: one envelope with a fixed code you can branch on, and codes that tell a trade that never ran from one that may have. The codes are the ones MetaTrader accounts answer, plus a few that only the cTrader bridge sends.
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 path returns a plain 404 from the edge. Everything else comes back in the same envelope: an error code, a message, and a command_id for log correlation. The code is fixed, so branch on it, never on the message:
{
"error": "MRPC_UNKNOWN_SYMBOL",
"message": "unknown symbol: EURUSDX",
"command_id": 19
}| Code | HTTP | Meaning |
|---|---|---|
UNAUTHORIZED | 401 | Missing or wrong API key. |
READ_ONLY_KEY | 403 | A read-only key on a route that trades or changes the terminal. |
MRPC_VALIDATION | 400 | A parameter is missing or malformed, or the order is one cTrader can't take (a stop-limit order, execution Manual). Nothing was sent. |
MRPC_UNKNOWN_TICKET | 400 | No order or position with that ticket. |
MRPC_UNKNOWN_SYMBOL | 400 | The broker has no symbol by that name. Check /symbols for the exact spelling. |
MRPC_BAD_TIMEFRAME | 400 | The timeframe isn't one of the nine the API serves. |
MRPC_CLIENT_ORDER_ID_REUSED | 409 | The clientOrderId was already used for a different order in the last 10 minutes. Nothing was sent. |
MRPC_SYMBOL_NOT_QUOTING | 400 | cTrader has no price for the symbol yet, for example while its market is closed. Retry shortly. |
MRPC_TRADE_EA_NOT_READY | 503 | The account is still signing in to cTrader, typically for a few seconds after linking or a restart. Nothing was executed; retry. |
MRPC_ABANDONED | 504 | The request timed out before the account's trade engine took it. Not executed, safe to resend. |
MRPC_TIMEOUT | 504 | A read timed out. |
MRPC_TRADE_OUTCOME_UNKNOWN | 504 | A trade went to cTrader and no answer came back, even after the bridge looked the order up, so it may have executed. Check /OpenedOrders before you send it again, or resend it with the same clientOrderId. |
MRPC_CALC_FAILED | 400 | cTrader couldn't compute the margin or profit, for example without a conversion rate to the account currency. Retry later. |
ACCOUNT_NOT_FOUND | 404 | The bridge doesn't serve this account right now: it was removed, is suspended, or its cTrader sign-in was revoked. Check the account with GET /v1/accounts/{account_id}. |
BROKER_UNAVAILABLE | 503 | The connection to cTrader is down or being rebuilt. Nothing was sent; retry shortly. |
BROKER_TIMEOUT | 504 | cTrader didn't answer a read in time. Retry. |
CTRADER_<code> | 500 | cTrader refused a request for a reason that isn't a trade result, for example an expired sign-in (CTRADER_CH_ACCESS_TOKEN_INVALID). The message is cTrader's. |
MRPC_INTERNAL / MRPC_UNKNOWN_COMMAND | 500 | A fault in the bridge. Quote the command_id when you report it. |
Two of these tell you whether a trade ran. MRPC_ABANDONED means it didn't, so sending it again is safe. MRPC_TRADE_OUTCOME_UNKNOWN means it may have, so never resend that order blindly. Send every order with a clientOrderId and the question goes away: a repeat with the same id never trades twice and returns the first reply instead (see Trading).
cTrader 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. cTrader names its refusals with codes such as NOT_ENOUGH_MONEY, so the bridge translates each into the matching MT5 retcode and puts cTrader's message in comment. Your code can branch on the same retcodes for cTrader, MT4 and MT5 accounts:
{
"success": false,
"outcome": "rejected",
"retcode": 10014,
"retcodeDescription": "Invalid volume",
"deal": 0,
"order": 0,
"comment": "volume 0.015 is not a multiple of 0.01 lots from 0.01 to 100",
...
}| cTrader error | retcode |
|---|---|
MARKET_CLOSED, SYMBOL_HAS_HOLIDAY | 10018 Market closed |
NOT_ENOUGH_MONEY | 10019 No money |
TRADING_BAD_VOLUME, TRADING_BAD_STAKE | 10014 Invalid volume |
TRADING_BAD_PRICES | 10015 Invalid price |
TRADING_BAD_STOPS, PROTECTION_IS_TOO_CLOSE_TO_MARKET | 10016 Invalid stops |
TRADING_BAD_EXPIRATION_DATE | 10022 Invalid expiration |
TRADING_DISABLED, TRADING_NOT_ALLOWED | 10017 Trade disabled |
SHORT_SELLING_NOT_ALLOWED | 10042 Long only |
NO_QUOTES | 10021 No prices |
MAX_EXPOSURE_REACHED | 10034 Volume limit |
TOO_MANY_POSITIONS | 10040 Positions limit |
POSITION_LOCKED, PENDING_EXECUTION | 10028 Locked |
POSITION_NOT_FOUND, POSITION_NOT_OPEN | 10036 Position closed |
ORDER_NOT_FOUND | 10035 Invalid order |
REQUEST_FREQUENCY_EXCEEDED | 10024 Too many requests |
CANT_ROUTE_REQUEST, SERVER_IS_UNDER_MAINTENANCE | 10031 No connection |
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. A volume off the symbol's step (10014), a pending price on the wrong side of the market (10015) and a modify that changes nothing (10025) are answered by the bridge before anything goes to cTrader.
Orders cTrader cannot take the way MetaTrader means them never reach the broker: a stop-limit order (BuyStopLimit, SellStopLimit) and execution: "Manual" are answered with HTTP 400 and MRPC_VALIDATION, with the reason in message.
Rate Limits
cTrader accounts reach Spotware through FxSocket's connections to the Open API, which limits requests per connection, history requests most tightly. The bridge paces requests within those limits, so a burst waits a little rather than failing, and it keeps closed bars, so asking for the same price history again costs nothing. A trade that cTrader still refuses for frequency comes back as retcode 10024. 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.