Bulk операции
Bulk-операции позволяют обрабатывать до 100 объявлений за один запрос. Используйте их для первичной синхронизации и массовых обновлений.
Зачем использовать bulk
| Подход | Запросов на 100 объявлений |
|---|---|
| Отдельные запросы | 100 |
| Bulk-операция | 1 |
Bulk снижает накладные расходы на сеть и ускоряет синхронизацию.
Типы операций
Каждая операция должна содержать поле op, определяющее тип действия:
| Операция | Описание |
|---|---|
upsert | Создать или обновить объявление |
link | Привязать external ID к существующему объявлению |
unlink | Отвязать external ID |
delete | Удалить объявление |
validate | Проверить данные без сохранения |
smart_create | Создать из текста + фото с помощью ИИ |
Создайте или обновите объявления
Операция upsert создаёт новое объявление или обновляет существующее по externalId.
Используйте externalUrl для точной публичной страницы объявления на исходном сайте. Это должен быть абсолютный HTTPS URL без данных авторизации длиной не более 2048 символов. Передайте null, чтобы удалить ссылку.
const response = await fetch('https://crm.rentix.md/api/v1/listings/bulk', {
method: 'POST',
headers: {
'Authorization': 'ApiKey YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
operations: [
{
op: 'upsert',
externalId: 'apt-001',
externalUrl: 'https://agency.example/listings/kvartira-v-centre',
announcementType: 'rent',
propertyType: 'residential',
propertySecondaryType: 'apartment',
announcementValue: 500,
announcementCurrency: 'EUR',
announcementDescription: 'Уютная квартира в центре...',
files: [
{ url: 'https://example.com/photo1.jpg', externalFileId: 'apt-001-photo-1' },
{ url: 'https://example.com/photo2.jpg', externalFileId: 'apt-001-photo-2' },
{ url: 'https://example.com/photo3.jpg', externalFileId: 'apt-001-photo-3' }
],
announcementStatus: 'active'
},
{
op: 'upsert',
externalId: 'apt-002',
externalUrl: 'https://agency.example/listings/kvartira-s-balkonom',
announcementType: 'rent',
propertyType: 'residential',
propertySecondaryType: 'apartment',
announcementValue: 700,
announcementCurrency: 'EUR',
announcementDescription: 'Просторная квартира с балконом...',
files: [
{ url: 'https://example.com/photo4.jpg', externalFileId: 'apt-002-photo-1' },
{ url: 'https://example.com/photo5.jpg', externalFileId: 'apt-002-photo-2' },
{ url: 'https://example.com/photo6.jpg', externalFileId: 'apt-002-photo-3' }
],
announcementStatus: 'active'
}
]
})
});
const result = await response.json();
console.log(`Успешно: ${result.summary.succeeded}/${result.summary.total}`);
curl -X POST https://crm.rentix.md/api/v1/listings/bulk \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"operations": [
{
"op": "upsert",
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre",
"announcementStatus": "active",
"announcementType": "rent",
"propertyType": "residential",
"propertySecondaryType": "apartment",
"announcementValue": 500,
"announcementCurrency": "EUR",
"files": [
{ "url": "https://example.com/photo1.jpg", "externalFileId": "apt-001-photo-1" }
]
}
]
}'
$data = [
'operations' => [
[
'op' => 'upsert',
'externalId' => 'apt-001',
'externalUrl' => 'https://agency.example/listings/kvartira-v-centre',
'announcementType' => 'rent',
'propertyType' => 'residential',
'propertySecondaryType' => 'apartment',
'announcementValue' => 500,
'announcementCurrency' => 'EUR',
'announcementStatus' => 'active',
'files' => [
['url' => 'https://example.com/photo1.jpg', 'externalFileId' => 'apt-001-photo-1']
]
]
]
];
$ch = curl_init('https://crm.rentix.md/api/v1/listings/bulk');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: ApiKey YOUR_API_KEY',
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
{
"results": [
{
"op": "upsert",
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre",
"id": 42,
"success": true,
"created": true,
"updated": false
},
{
"op": "upsert",
"externalId": "apt-002",
"externalUrl": "https://agency.example/listings/kvartira-s-balkonom",
"id": 43,
"success": true,
"created": true,
"updated": false
}
],
"summary": {
"total": 2,
"succeeded": 2,
"failed": 0
}
}
Обновление только исходных URL
upsert, содержащий только op, externalId и externalUrl, обновляет исходную ссылку, не изменяя само объявление:
{
"operations": [
{
"op": "upsert",
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre"
},
{
"op": "upsert",
"externalId": "apt-002",
"externalUrl": null
}
]
}
{
"results": [
{
"op": "upsert",
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre",
"id": 42,
"success": true,
"updated": true
},
{
"op": "upsert",
"externalId": "apt-002",
"externalUrl": null,
"id": 43,
"success": true,
"updated": true
}
],
"summary": {
"total": 2,
"succeeded": 2,
"failed": 0
}
}
Привяжите external ID
Операция link связывает ваш ID из CRM с существующим объявлением в Rentix. Используйте её для объявлений, созданных через веб-интерфейс.
{
"operations": [
{ "op": "link", "id": 42, "externalId": "apt-001" },
{ "op": "link", "id": 43, "externalId": "apt-002" }
]
}
{
"results": [
{ "op": "link", "id": 42, "externalId": "apt-001", "success": true, "linked": true },
{ "op": "link", "id": 43, "externalId": "apt-002", "success": true, "linked": true }
]
}
Отвяжите external ID
Операция unlink удаляет связь между объявлением и external ID.
{
"operations": [
{ "op": "unlink", "id": 42 },
{ "op": "unlink", "externalId": "apt-002" }
]
}
Удалите объявления
Операция delete удаляет объявления.
{
"operations": [
{ "op": "delete", "externalId": "apt-old-001" },
{ "op": "delete", "id": 99 }
]
}
{
"results": [
{ "op": "delete", "externalId": "apt-old-001", "success": true, "deleted": true }
]
}
Проверьте данные
Операция validate проверяет данные без сохранения. Используйте для валидации форм в CRM.
{
"operations": [
{
"op": "validate",
"externalId": "apt-new",
"announcementType": "rent",
"propertyType": "residential",
"propertySecondaryType": "apartment",
"announcementValue": 500
}
]
}
{
"results": [
{
"op": "validate",
"externalId": "apt-new",
"success": true,
"valid": true,
"errors": [],
"warnings": []
}
]
}
Smart Create
Операция smart_create создаёт объявление из текстового описания и фотографий с помощью ИИ. Принимает те же параметры, что и POST /listings/smart.
{
"operations": [
{
"op": "smart_create",
"description": "Уютная 2-комнатная квартира, 65 м², этаж 3/9. Евроремонт, тёплые полы.",
"files": [
{ "url": "https://example.com/photo1.jpg", "externalFileId": "apt-001-photo-1" }
],
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre",
"targetStatus": "active"
}
]
}
{
"results": [
{
"op": "smart_create",
"externalId": "apt-001",
"externalUrl": "https://agency.example/listings/kvartira-v-centre",
"success": true,
"jobId": 789,
"status": "pending"
}
]
}
Каждая операция smart_create создаёт асинхронную задачу. Отслеживайте прогресс через статус задачи. Подробнее о параметрах — в разделе Smart Create.
Комбинируйте операции
В одном запросе можно выполнить разные типы операций:
{
"operations": [
{ "op": "upsert", "externalId": "apt-001", "announcementValue": 550 },
{ "op": "upsert", "externalId": "apt-002", "announcementStatus": "hidden" },
{ "op": "link", "id": 99, "externalId": "apt-003" },
{ "op": "delete", "externalId": "apt-old" }
]
}
HTTP-статусы ответа
HTTP-статус ответа bulk-запроса зависит от результата отдельных операций:
| Статус | Условие | Описание |
|---|---|---|
| 200 | Все операции успешны | Каждая операция имеет success: true |
| 207 | Частичный успех | Часть операций успешна, часть — нет |
| 422 | Все операции неуспешны | Каждая операция имеет success: false |
Всегда проверяйте поле summary и отдельные results независимо от HTTP-статуса.
Обработка ошибок
Ошибка в одной операции не останавливает остальные. Проверяйте success для каждого результата.
{
"results": [
{ "op": "upsert", "externalId": "apt-001", "id": 42, "success": true },
{
"op": "upsert",
"externalId": "apt-002",
"id": null,
"success": false,
"error": {
"statusCode": 400,
"body": {
"error": "propertyType is required",
"error_code": "VALIDATION_ERROR"
}
}
},
{ "op": "upsert", "externalId": "apt-003", "id": 44, "success": true }
],
"summary": {
"total": 3,
"succeeded": 2,
"failed": 1
}
}
Обработайте неуспешные операции:
const result = await response.json();
const failed = result.results.filter(r => !r.success);
if (failed.length > 0) {
console.error('Ошибки:', failed);
// Логировать или повторить позже
}
Рекомендации
Отправляйте последовательно
Отправляйте пакеты последовательно, а не параллельно. Система обрабатывает операции в порядке очереди — параллельная отправка не ускорит обработку.
Обновляйте только изменённые данные
После первичной синхронизации отправляйте только изменённые объявления. Повторная отправка всех объявлений без изменений — неэффективное использование API.
Используйте частичные обновления
Передавайте только изменённые поля. Чтобы очистить поле, отправьте null:
{
"op": "upsert",
"externalId": "apt-001",
"announcementValue": 600,
"propertyFloorNumber": null
}
Ограничения
| Параметр | Значение |
|---|---|
| Максимум операций в запросе | 100 |
Поля операции upsert | см. PUT /listings |
Коды ошибок
| Код | Описание | Решение |
|---|---|---|
VALIDATION_ERROR | Ошибка валидации полей | Проверьте обязательные поля и форматы |
USER_RESOLUTION_FAILED | Агент не найден | Проверьте userId или externalUserId |
NOT_FOUND | Объявление не найдено | Используйте externalId для upsert |
EXTERNAL_ID_ALREADY_LINKED | External ID уже используется | Используйте уникальный external ID |