Trader
Tools

Errors

The problem details format, which failures are worth retrying, and every error code grouped by HTTP status with what to do about it.

View as Markdown

When a request fails, the API returns problem details, a standard JSON error format (RFC 9457), with the content type application/problem+json. Every error has a stable code. Let your code check code, never the title or detail, which are written for people to read.

{
  "type": "https://algotradertools.com/docs/errors#validation_failed",
  "title": "Validation failed",
  "status": 400,
  "code": "validation_failed",
  "detail": "lot_value: Use a number above 0 and up to 1000.",
  "errors": [
    { "path": "/lot_value", "code": "invalid", "message": "Use a number above 0 and up to 1000." }
  ],
  "request_id": "b7c1e2d4-5f6a-4b8c-9d0e-1f2a3b4c5d6e",
  "doc_url": "https://algotradertools.com/docs/errors#validation_failed"
}
FieldDescription
typeA URI for the error type. It points to this page, at the code's row
titleA short human-readable summary
statusThe HTTP status code, repeated in the body
codeThe stable identifier to branch on. Every code is listed below
detailWhat went wrong in this particular case
errorsOnly for validation_failed. One entry per problem, each with a path to the field as a JSON Pointer ("" for the request as a whole), a field-level code and a message
request_idThe request's ID, also in the X-Request-Id header. Include it when you contact support
doc_urlA link to this page, at the code's row

Handling errors

Retry these:

  • 429 after the number of seconds in Retry-After
  • 500, 502, 503 and 504, with exponential backoff

Don't retry other errors without changing the request first. A 400, 401, 403, 404, 405, 406, 409 or 415 will fail the same way again.

Retrying a POST is safe for accounts and followers. If the first attempt went through, the second fails with already_connected or already_following instead of creating a duplicate. A retried POST /v1/copiers can create a second copier. If you didn't get a response to one, list your copiers before you retry it.

const API = "https://api.algotradertools.com/v1";
const MAX_ATTEMPTS = 5;

async function request(method: string, path: string, body?: unknown) {
  for (let attempt = 1; ; attempt++) {
    const response = await fetch(`${API}${path}`, {
      method,
      headers: {
        Authorization: `Bearer ${process.env.ATT_API_KEY}`,
        ...(body === undefined ? {} : { "Content-Type": "application/json" }),
      },
      body: body === undefined ? undefined : JSON.stringify(body),
    });
    if (response.status === 204) return null;
    const payload = await response.json();
    if (response.ok) return payload;

    const retryable = response.status === 429 || response.status >= 500;
    if (!retryable || attempt === MAX_ATTEMPTS) {
      throw new Error(`${payload.code}: ${payload.detail} (request ${payload.request_id})`);
    }
    const retryAfter = Number(response.headers.get("Retry-After"));
    const wait = retryAfter > 0 ? retryAfter * 1000 : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, wait));
  }
}

400 Bad Request

CodeMeaningFix
validation_failedA field is missing or invalidRead errors: each entry points at a field and has a field-level code
invalid_jsonThe body isn't valid JSONCheck quoting and encoding. In shells, watch for unescaped quotes inside -d '…'
invalid_cursorThe cursor isn't one the API returnedPass next_cursor from the previous page unchanged, or leave cursor out to start again
invalid_credentialsThe broker rejected the login and passwordCheck the account number and password, and that credential_kind matches the password you sent
server_not_foundThere's no broker server with that nameCopy the server exactly as MetaTrader shows it in its login window
already_connectedThat MetaTrader account is already connectedList your accounts; if it isn't yours, contact support
proxy_unreachableNothing could be reached through the account's proxyCheck the proxy's type, host and port, username and password, and that it's online
connect_rejectedThe account was refused for another reasonRead detail

401 Unauthorized

CodeMeaningFix
authentication_requiredNo API key was sentSend Authorization: Bearer <key> or the X-API-Key header
invalid_api_keyThe key is unknown or malformedCheck for truncation and stray whitespace, or create a new key
api_key_revokedThe key was revokedCreate a new key and deploy it

403 Forbidden

CodeMeaningFix
insufficient_scopeThe key lacks the scope this endpoint needsUse a key with the right scope, for example copiers:write
plan_limit_reachedEvery account slot of your plan is in use, or you don't have a plan yetDisconnect an account, or choose a bigger plan in the app

404 Not Found

CodeMeaningFix
not_foundNo such account, copier or follower, or it belongs to someone else; or no such endpointCheck the ID and the path. Paths have no trailing slash

405 Method Not Allowed

CodeMeaningFix
method_not_allowedThe endpoint doesn't support this HTTP methodSee the API reference for the methods of each path

406 Not Acceptable

CodeMeaningFix
not_acceptableThe Accept header rules out JSONSend Accept: application/json, or leave the header out

409 Conflict

CodeMeaningFix
account_in_useThe account is a copier's master or follower, so it can't be disconnectedDelete the copier or remove the follower first
account_not_releasedOnly an account whose terminal was released can be reconnectedNothing to do: the account still has its terminal

415 Unsupported Media Type

CodeMeaningFix
unsupported_media_typeThe body isn't sent as JSONSend Content-Type: application/json

429 Too Many Requests

CodeMeaningFix
rate_limitedToo many requests for the key, or too many account connects, reconnects and disconnects for your userWait the number of seconds in Retry-After. See Rate limits

500 and above

StatusCodeMeaningFix
500internal_errorSomething broke on our sideRetry with backoff. If it persists, contact support with the request_id
502, 503 or 504close_failedCopies couldn't be closed while deleting a copier or removing a followerNothing was deleted. Every follower handled up to the failure is now paused, and all of them except the one that failed already had their copies closed. Retry the delete, or resume the followers
502, 503 or 504disconnect_failedThe account's terminal couldn't be stoppedThe account was kept. Retry shortly
502terminal_unreachableThe account's terminal couldn't be reachedRetry with backoff
503terminal_unavailableThe terminal is still starting up or busyRetry shortly
503connect_unavailableNew accounts can't be connected right nowRetry later
504terminal_timeoutThe terminal didn't answer in timeRetry shortly

Field-level codes

A validation_failed error lists each problem in errors, with a path to the field and a code. Besides the generic codes required, invalid, null, blank, max_length, invalid_choice, does_not_exist, incorrect_type, min_value, max_digits and max_decimal_places, these say exactly what is wrong:

CodeFieldMeaning
already_connectedloginYou already connected this MetaTrader account
account_releasedloginYou connected it before and its terminal was released. Reconnect it instead
account_not_connectedmaster_id, account_idThe account is still connecting or was released
account_is_masteraccount_idThe account is a copier's master. An account can't be both a master and a follower
account_is_followermaster_idThe account follows another copier. An account can't be both a master and a follower
already_followingaccount_idThe account already copies this master, in this copier or another one
self_copyaccount_idThe copier's master can't follow itself
read_only_accountaccount_idThe account was connected with its investor password and can't place trades
netting_accountmaster_id, account_idMT5 netting accounts aren't supported, only hedging accounts
automated_trading_offaccount_idThe broker has switched off trading, or automated trading (Expert Advisors), on this account, so copies can't be placed. Ask the broker to allow it
immutablemaster_id, account_idThe field can't be changed after creation. Create a new copier or follower instead
unknown_fieldAnyThe body has a field the resource doesn't have, often a typo. Read-only fields such as id or status are accepted and ignored, so you can send an object back as you read it

A close_positions query parameter that isn't true, false, 1 or 0 fails with validation_failed too, with the path /close_positions.

On this page