Ошибки

Обработка ошибок

HTTP-статусы, rate limiting, retry-стратегии и типичные ошибки

API использует стандартные HTTP-коды для сигнализации об ошибках. Все ошибки возвращают JSON с описанием проблемы.

Формат ошибки

{
  "statusCode": 400,
  "message": "Описание ошибки",
  "error": "Bad Request"
}

Для ошибок валидации message может быть массивом:

{
  "statusCode": 400,
  "message": [
    "announcementDescription must be at least 40 characters",
    "propertyType must be one of: residential, commercial, parking"
  ],
  "error": "Bad Request"
}

HTTP-статусы

КодТипОписаниеПовторять запрос?
400Bad RequestНеверные параметры запросаНет — исправьте данные
401UnauthorizedПроблема с API ключомНет — проверьте ключ
403ForbiddenНет доступа к ресурсуНет — проверьте права
404Not FoundРесурс не найденНет — проверьте ID
415Unsupported Media TypeНеверный Content-TypeНет — используйте application/json
422Unprocessable EntityВсе bulk-операции завершились ошибкойНет — исправьте данные
429Too Many RequestsПревышен лимит запросовДа — после паузы
500Server ErrorВнутренняя ошибкаДа — с exponential backoff

Требования к запросам

Content-Type

Все запросы с телом (POST, PUT, PATCH) должны содержать заголовок Content-Type: application/json. Запросы с другими типами содержимого получают ответ 415 Unsupported Media Type.

Ограничение размера тела

Размер тела запроса ограничен 512 КБ. Запросы, превышающие этот лимит, получают ответ 413 Payload Too Large.

Rate Limiting

API ограничивает количество запросов для защиты от перегрузки. Каждый ответ содержит заголовки с информацией о лимитах.

Тип endpointЛимит
Глобальный (все endpoints)300 запросов / минута
Bulk endpoints30 запросов / минута

Заголовки лимитов

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295
X-RateLimit-Reset: 1705330800
ЗаголовокОписание
X-RateLimit-LimitМаксимум запросов за период
X-RateLimit-RemainingСколько запросов осталось
X-RateLimit-ResetКогда сбросится лимит (Unix timestamp)

Что делать при 429

При превышении лимита API вернёт:

{
  "statusCode": 429,
  "message": "Too Many Requests"
}

Дождитесь времени из X-RateLimit-Reset и повторите запрос:

Node.js
async function fetchWithRateLimit(url, options) {
  const response = await fetch(url, options);

  if (response.status === 429) {
    const resetTime = response.headers.get('X-RateLimit-Reset');
    const waitMs = (parseInt(resetTime) * 1000) - Date.now();
    await new Promise(r => setTimeout(r, Math.max(waitMs, 1000)));
    return fetch(url, options); // Повторить
  }

  return response;
}

Retry-стратегия

Когда повторять запросы

СитуацияДействие
5xx ошибкиПовторить с exponential backoff
429 ошибкиПодождать до X-RateLimit-Reset
Сетевые ошибкиПовторить с backoff
4xx ошибкиНе повторять — исправить запрос

Exponential backoff

При серверных ошибках увеличивайте паузу между попытками:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      // Серверная ошибка — повторить
      if (response.status >= 500) {
        throw new Error(`Server error: ${response.status}`);
      }

      return response;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;

      // Exponential backoff: 1s, 2s, 4s
      const delay = 1000 * Math.pow(2, attempt);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

// Использование
const response = await fetchWithRetry(
  'https://crm.rentix.md/api/v1/listings',
  {
    method: 'PUT',
    headers: { 'Authorization': 'ApiKey YOUR_API_KEY' },
    body: JSON.stringify(data)
  }
);

Типичные ошибки

Аутентификация (400, 401, 403)

ОшибкаПричинаРешение
Multiple authentication methods providedПереданы оба заголовка Authorization и X-API-KeyИспользуйте только один заголовок аутентификации
API key is requiredAPI ключ не предоставленДобавьте заголовок Authorization: ApiKey YOUR_KEY
Invalid API keyAPI ключ неверен или отозванПроверьте значение ключа
Agency is suspendedАккаунт агентства приостановленСвяжитесь с поддержкой
CRM is not enabled for this agencyCRM доступ не подключенСвяжитесь с поддержкой для подключения CRM

Объявления (400)

ОшибкаПричинаРешение
Cannot provide both id and externalIdПередали оба идентификатораИспользуйте один из них
Must provide id or externalIdНе передали идентификатор для обновленияДобавьте id или externalId
External ID already linkedExternal ID занят другим объявлениемИспользуйте уникальный ID
Minimum 3 photos requiredДля публикации нужны фотоЗагрузите минимум 3 фото
Description too shortОписание меньше 40 символовДобавьте больше текста

Медиафайлы (400)

ОшибкаПричинаРешение
Duplicate external IDExternal ID уже используетсяИспользуйте уникальный ID
File not foundФайл не существуетПроверьте fileId или externalFileId
Invalid file typeНеподдерживаемый форматИспользуйте JPEG, PNG, WebP или HEIC
Upload URL expiredSigned URL истёк (30 минут)Запросите новый URL

Валидация полей

КодОписание
STRING_TOO_SHORTСтрока короче минимума
STRING_TOO_LONGСтрока длиннее максимума
NUMBER_TOO_SMALLЧисло меньше минимума
NUMBER_TOO_LARGEЧисло больше максимума
INVALID_ENUM_VALUEЗначение не из списка допустимых
REQUIRED_FIELDОбязательное поле не заполнено

Пример: обработка всех случаев

Node.js
async function apiRequest(endpoint, data) {
  const url = `https://crm.rentix.md/api/v1${endpoint}`;
  const options = {
    method: 'PUT',
    headers: {
      'Authorization': 'ApiKey YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  };

  for (let attempt = 0; attempt < 3; attempt++) {
    const response = await fetch(url, options);

    // Успех
    if (response.ok) {
      return response.json();
    }

    // Rate limit — ждём и повторяем
    if (response.status === 429) {
      const resetTime = response.headers.get('X-RateLimit-Reset');
      const waitMs = (parseInt(resetTime) * 1000) - Date.now();
      await new Promise(r => setTimeout(r, Math.max(waitMs, 1000)));
      continue;
    }

    // Серверная ошибка — повторяем с backoff
    if (response.status >= 500) {
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
      continue;
    }

    // Клиентская ошибка — не повторяем
    const error = await response.json();
    throw new Error(`API Error: ${JSON.stringify(error.message)}`);
  }

  throw new Error('Max retries exceeded');
}

Рекомендации

  1. Логируйте ошибки — сохраняйте statusCode, message и timestamp для диагностики
  2. Не игнорируйте 4xx — это ошибки в ваших данных, исправьте их
  3. Уважайте rate limits — при 429 всегда ждите указанное время
  4. Используйте exponential backoff — при 5xx не долбите сервер сразу
Copyright © 2026