Ошибки
Обработка ошибок
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-статусы
| Код | Тип | Описание | Повторять запрос? |
|---|---|---|---|
| 400 | Bad Request | Неверные параметры запроса | Нет — исправьте данные |
| 401 | Unauthorized | Проблема с API ключом | Нет — проверьте ключ |
| 403 | Forbidden | Нет доступа к ресурсу | Нет — проверьте права |
| 404 | Not Found | Ресурс не найден | Нет — проверьте ID |
| 415 | Unsupported Media Type | Неверный Content-Type | Нет — используйте application/json |
| 422 | Unprocessable Entity | Все bulk-операции завершились ошибкой | Нет — исправьте данные |
| 429 | Too Many Requests | Превышен лимит запросов | Да — после паузы |
| 500 | Server 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 endpoints | 30 запросов / минута |
Заголовки лимитов
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)
}
);
function fetchWithRetry($url, $options, $maxRetries = 3) {
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 500) {
return ['code' => $httpCode, 'body' => $response];
}
if ($attempt < $maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s
sleep(pow(2, $attempt));
}
}
throw new Exception("Max retries exceeded");
}
Типичные ошибки
Аутентификация (400, 401, 403)
| Ошибка | Причина | Решение |
|---|---|---|
Multiple authentication methods provided | Переданы оба заголовка Authorization и X-API-Key | Используйте только один заголовок аутентификации |
API key is required | API ключ не предоставлен | Добавьте заголовок Authorization: ApiKey YOUR_KEY |
Invalid API key | API ключ неверен или отозван | Проверьте значение ключа |
Agency is suspended | Аккаунт агентства приостановлен | Свяжитесь с поддержкой |
CRM is not enabled for this agency | CRM доступ не подключен | Свяжитесь с поддержкой для подключения CRM |
Объявления (400)
| Ошибка | Причина | Решение |
|---|---|---|
Cannot provide both id and externalId | Передали оба идентификатора | Используйте один из них |
Must provide id or externalId | Не передали идентификатор для обновления | Добавьте id или externalId |
External ID already linked | External ID занят другим объявлением | Используйте уникальный ID |
Minimum 3 photos required | Для публикации нужны фото | Загрузите минимум 3 фото |
Description too short | Описание меньше 40 символов | Добавьте больше текста |
Медиафайлы (400)
| Ошибка | Причина | Решение |
|---|---|---|
Duplicate external ID | External ID уже используется | Используйте уникальный ID |
File not found | Файл не существует | Проверьте fileId или externalFileId |
Invalid file type | Неподдерживаемый формат | Используйте JPEG, PNG, WebP или HEIC |
Upload URL expired | Signed 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');
}
Рекомендации
- Логируйте ошибки — сохраняйте
statusCode,messageи timestamp для диагностики - Не игнорируйте 4xx — это ошибки в ваших данных, исправьте их
- Уважайте rate limits — при 429 всегда ждите указанное время
- Используйте exponential backoff — при 5xx не долбите сервер сразу