Skip to content

Ошибки, retries и лимиты ​

Формат ошибок /api/v1/* ​

Все Public API v1 endpoints возвращают единый JSON envelope:

json
{
  "error": {
    "code": "invalid_signal",
    "message": "Signal validation failed.",
    "traceId": "0HN8A1BC23DEF:00000001",
    "details": [
      {
        "field": "ttlMs",
        "code": "signal_expired"
      }
    ]
  },
  "requestId": "optional_request_correlation"
}
ПолеНазначение
error.codeстабильный machine-readable code
error.messageтекст для диагностики; не используйте для ветвления программы
error.traceIdcorrelation server log; указывайте при обращении в поддержку
error.detailsмассив ошибок отдельных полей, обычно только для invalid_signal
requestIdприсутствует, когда backend уже успел создать или найти signal request

Всегда принимайте неизвестный error.code как допустимое расширение API и сохраняйте полный response для диагностики.

Чтение ошибки в трёх языках ​

bash
curl --silent --show-error \
  --dump-header /tmp/tmpro-headers.txt \
  --output /tmp/tmpro-body.json \
  'https://tradingmonitor.pro/api/v1/signals/REPLACE_WITH_REQUEST_ID' \
  --header 'Authorization: Bearer tmpro_api_REPLACE_ME'

cat /tmp/tmpro-headers.txt
cat /tmp/tmpro-body.json
csharp
using System.Text.Json;

using var response = await http.SendAsync(request);
var text = await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
{
    using var json = JsonDocument.Parse(text);
    var root = json.RootElement;
    var error = root.GetProperty("error");
    var code = error.GetProperty("code").GetString();
    var traceId = error.GetProperty("traceId").GetString();
    var requestId = root.TryGetProperty("requestId", out var id)
        ? id.GetString()
        : null;
    var retryAfter = response.Headers.RetryAfter?.Delta;

    Console.Error.WriteLine(
        $"HTTP {(int)response.StatusCode}; code={code}; " +
        $"requestId={requestId}; traceId={traceId}; retryAfter={retryAfter}");
}
python
response = requests.get(url, headers=headers, timeout=30)

if not response.ok:
    body = response.json()
    error = body.get("error", {})
    print(
        "HTTP", response.status_code,
        "code=", error.get("code"),
        "requestId=", body.get("requestId"),
        "traceId=", error.get("traceId"),
        "Retry-After=", response.headers.get("Retry-After"),
    )

Общие коды Public API v1 ​

HTTPCodeЗначение
401invalid_api_keybearer key отсутствует, неверен, истёк, отозван или пользователь удалён
403insufficient_scopekey действителен, но требуемый scope отсутствует
429api_rate_limitedпревышен rate limit API key или лимит неаутентифицированных попыток
500internal_errorнеобработанная server error до начала response
503public_api_disabledоператор временно отключил Public API v1

Authorization должен иметь вид:

http
Authorization: Bearer tmpro_api_<secret>

Public API key нельзя заменить пользовательским JWT или sessionToken.

Коды signal endpoints ​

POST /api/v1/signals ​

HTTPCodeRetry
400invalid_idempotency_keyисправить header; автоматический retry того же неверного запроса бесполезен
400malformed_jsonисправить JSON
403account_frozenнет, требуется изменение состояния аккаунта
409request_in_progressда, повторить тот же body и key после Retry-After
409idempotency_conflictнет; key уже связан с другим body
409idempotency_outcome_unknownне отправлять новую торговую операцию; проверить terminal/broker
409duplicate_signal_idнет; signalId уже использован
413request_body_too_largeуменьшить body
415unsupported_media_typeустановить Content-Type: application/json
422invalid_signalисправить поля по error.details
429signal_rate_limit_exceededда, после Retry-After; account quota
429daily_signal_limit_exceededтолько после сброса/изменения account quota
500signal_processing_failedне использовать новый key; outcome мог произойти

GET /api/v1/signals/{requestId} ​

HTTPCodeЗначение
404request_not_foundrequest отсутствует или принадлежит другому пользователю

Validation details для invalid_signal ​

fieldcodeПричина
$object_requiredJSON root не является object
vunsupported_versionотсутствует integer 1
signalIdinvalid_lengthотсутствует, пустой или длиннее 128 символов
typeinvalid_lengthотсутствует, пустой или длиннее 64 символов
cmdinvalid_lengthотсутствует, пустой или длиннее 64 символов
tsinvalid_unix_millisecondsзначение отсутствует, не integer или вне диапазона Unix milliseconds
tssignal_from_futuretimestamp дальше допустимого clock skew
ttlMsinvalid_ttlне integer либо вне 1..300000
ttlMssignal_expiredts + ttlMs уже в прошлом
ttlMstimestamp_overflowсумма timestamp и TTL вне допустимого диапазона
platformrequiredполе отсутствует или пустое
platformunsupported_platformодин из comma-separated tokens неизвестен
payloadobject_requiredpayload отсутствует или не object
instanceIdinvalid_lengthесли поле передано, оно пустое, не string или длиннее 128 символов

Ошибки terminal-specific payload, например неизвестный executionType или отсутствие limitPrice, возникают уже после принятия envelope. Они отражаются в executions[].status, reasonCode и terminal events, а не как HTTP 422.

Полный перечень terminal-specific полей и platform differences приведён в Справочнике торговых команд.

Коды exact cancel ​

HTTPCodeЗначение
400invalid_idempotency_keyнет корректного единственного header
404order_not_foundorderRef не найден, чужой или имеет недопустимый формат
409order_not_cancellableордер не находится в отменяемом tracked state
409terminal_unavailableexact target сейчас недоступен или не поддерживает cancel capability
409cancel_already_in_progressпредыдущая отмена ещё не финальна
409idempotency_conflictkey уже использован для другого order

Финальный broker/terminal outcome является не HTTP-ошибкой, а executions[].cancel.status. См. Точная отмена ордера.

Коды Terminal Events API ​

HTTPCodeЗначение
400invalid_terminal_sourcequery не задаёт точные platform, instanceId, accountName
400invalid_event_cursorLast-Event-ID повторён или длиннее 80 символов
404terminal_source_not_foundsource отсутствует среди активных sources пользователя
429too_many_event_subscriptionsуже открыты 3 SSE subscriptions пользователя

replay_gap передаётся как SSE event после успешного HTTP 200, а не как HTTP error:

text
event: replay_gap
data: {"reason":"cursor_not_available"}

Ошибки login и API-key management ​

Endpoints /api/auth/login и /api/api-keys используют более простой legacy формат:

json
{
  "code": "invalid_credentials",
  "message": "Invalid email or password."
}

Он не содержит error, traceId или details.

Login ​

HTTPCodeЗначение
400ASP.NET validation responseemail/password не прошли model validation
401invalid_credentialsневерный email или password
403email_not_confirmedemail не подтверждён
429rate_limit_exceededболее 10 попыток в минуту для client partition

API-key management ​

EndpointHTTPCodeЗначение
все401может отсутствоватьJWT отсутствует, истёк или недействителен
все403business_plan_requiredтекущий plan не Business
create400invalid_nameимя не содержит 1–80 символов после trim
create400invalid_scopesscope отсутствует, неизвестен или повторён
create400invalid_expirationexpiration не в будущем или дальше 365 дней
create400active_key_limit_reachedуже существует 5 активных keys
create503public_api_disabledсоздание keys временно отключено
delete404может отсутствоватьkey id не существует или принадлежит другому пользователю

Правила retry ​

Безопасный алгоритм для mutating request ​

  1. До запроса создайте Idempotency-Key и сохраните его вместе с byte-identical signal body или точным orderRef.
  2. При network timeout повторите тот же запрос с тем же key.
  3. При request_in_progress дождитесь Retry-After и повторите тот же запрос.
  4. При 429 дождитесь Retry-After, если он есть, затем примените exponential backoff с jitter.
  5. При 5xx повторяйте только идемпотентно — с тем же key и теми же данными.
  6. При idempotency_outcome_unknown или signal_processing_failed не создавайте новый key для той же торговой операции.

Для read-only GET безопасен обычный exponential backoff. Для SSE используйте Last-Event-ID и интервалы 1, 2, 5, 10 секунд.

Пример backoff ​

text
attempt 1: 1 s + jitter
attempt 2: 2 s + jitter
attempt 3: 5 s + jitter
attempt 4+: 10 s + jitter

Ограничивайте общее время retry и предоставляйте оператору traceId, requestId и Idempotency-Key без раскрытия API secret.

Текущие технические лимиты ​

Значения ниже соответствуют текущей backend-конфигурации и могут быть изменены оператором.

ЛимитЗначение
Signal request body65 536 bytes
JSON depth16
Idempotency-Key1–128 символов, без control characters
signalId1–128 символов
Signal instanceId1–128 символов, если поле передано; пустое значение запрещено
Максимальный ttlMs300 000 ms
Допустимое будущее смещение ts60 s
Активные API keys5 на пользователя
Авторизованные Public API requests1200/min на API key
Неавторизованные попытки60/min на client partition
Login10/min на client partition
Одновременные SSE streams3 на пользователя
SSE instanceIdдо 128 символов; пустая строка разрешена, параметр обязателен
SSE accountName1–128 символов
Last-Event-IDдо 80 символов
Terminal active heartbeat window120 s
Signal execution confirmation timeout60 s
Cancel confirmation timeout30 s
Signal idempotency retention24 h
Stale processing timeout5 min
Terminal event replay1000 events или 10 min на пользователя
Pending live events256 на SSE subscriber
Terminal event rawTextдо 8192 символов
MT5 outgoing terminal event queueдо 2000 events или 10 min
Completed execution retention30 days

Account signal quotas зависят от тарифного плана и применяются совместно к сигналам из Public API и других поддерживаемых каналов. SSE limit не влияет на количество подключённых устройств тарифного плана.

Безопасность логов ​

Не записывайте в application logs:

  • полный tmpro_api_... key;
  • пользовательский JWT;
  • password;
  • SignalR session token.

Можно записывать displayPrefix, requestId, executionId, orderRef, cancelRequestId, eventId, HTTP status, error.code и traceId.

TradingMonitor.Pro documentation.