Динамическое ценообразование в e-commerce: связка Bitrix, n8n и AI-аналитики

Зачем магазину на Bitrix динамическое ценообразование в 2026 году

Wildberries и Ozon уже несколько лет публично говорят о том, что цена влияет на позицию карточки в поиске. Точные формулы площадки не раскрывают, но практика показывает: отклонение от медианы категории на несколько процентов заметно снижает видимость карточки. Для конкурентного сегмента, где топ-10 забирает 70% кликов, это болезненно для органики.

Яндекс.Маркет работает по схожей логике, просто менее прозрачно.

На этом фоне у магазинов на Bitrix, которые торгуют параллельно на маркетплейсах и через собственный сайт, накопилось конкретное противоречие. На WB цена меняется алгоритмом площадки каждые несколько часов, а на сайте прайс-менеджер обновляет Excel раз в неделю. Покупатель видит разные цены, уходит на маркетплейс, и магазин теряет маржу без комиссии площадки, которую мог бы получить на прямой продаже.

Регуляторное давление тоже усилилось. Если магазин использует зачёркнутую "старую цену" в промо, вопросы со стороны Роспотребнадзора по документальному подтверждению истории цен стали обычной практикой. Вручную поддерживать такой лог при живом каталоге нереально. Нужна система, которая пишет историю автоматически.

Теперь про экономику. По результатам нескольких клиентских проектов: автоматизация пересчёта цен на медленных SKU может ускорить оборачиваемость остатков. Это значит, что деньги, замороженные в остатках, начинают работать быстрее. Валовая маржа при этом способна расти, потому что система не просто снижает цену, она поднимает её, когда конкуренты кончаются или спрос растёт.

Почему прайс-менеджер с этим не справится при каталоге от 5000 SKU. Математика простая: если у вас 50 активных конкурентов в нише и 5000 позиций, это 250 000 пар "товар-конкурент" для мониторинга. Даже при проверке раз в сутки один человек физически не успевает принять осмысленные решения по каждой. Он будет работать по укрупнённым категориям и интуиции, что неизбежно означает либо потерю маржи там, где можно держать цену выше, либо проседание в выдаче там, где надо было снизить.

Но есть случаи, где динамика реально не нужна. Если вы продаёте уникальный товар без прямых аналогов, ориентироваться по цене некому. B2B-контракты с фиксированными ценами на квартал или год тоже выведены из уравнения: клиент купил по договору, а не по прайсу сайта. И если ваша модель на маркетплейсе строится на FBO с минимальным вмешательством, алгоритм самой площадки уже делает ценовую оптимизацию за вас. Туда лезть своей логикой значит создавать конфликт двух систем.

Экраны с ценами конкурентов и аналитикой продаж на мониторах

Ручной мониторинг цен конкурентов съедает часы работы менеджеров каждую неделю, а автоматизация сокращает это время до минут.

Архитектура связки: Bitrix как ядро, n8n как оркестратор, AI как мозг

Разделение ролей у меня получилось такое: Bitrix, система записи (source of truth), n8n, клей и логика потоков, AI, слой принятия решений. Каждый занимается тем, что умеет лучше всего, и не лезет в чужую зону.

В Bitrix живёт каталог, остатки, себестоимость, история заказов и скидочные правила. Я туда ничего нового не складываю кроме одной таблицы, истории пересчётов цен (об этом ниже). Всё остальное берётся как есть через REST: catalog.product.get для карточек, catalog.price.get для текущих цен, sale.basket и sale.order.list для исторических продаж. По остаткам, catalog.store.product (уточняйте актуальные методы в документации Bitrix REST API).

n8n у меня крутится отдельно от Bitrix, на собственной VPS. Workflow примерно такой:

  1. Cron-нода будит процесс (чаще для топ-SKU, реже для хвоста).
  2. Batch-выгрузка товаров из Bitrix.
  3. Парсинг конкурентов: HTTP Request + кастомные парсеры на JS-нодах, для сложных сайтов отдельный воркер с Playwright.
  4. Обогащение данных через LLM.
  5. Прогон через ML-модель эластичности.
  6. Запись новой цены обратно и лог.

AI-слой я разнёс на две независимые штуки, потому что задачи разные. Для классификации товаров и интерпретации сигналов (это товар-локомотив или маржинальный хвост? у конкурента сейчас распродажа или регулярная цена?) гоняю GPT-4.1 либо Claude 3.7 Sonnet, в зависимости от того, где в этом месяце лучше латентность. А вот считать саму ценовую эластичность LLM не умеет, и пытаться заставить её это делать, деньги на ветер. Под эластичность у меня обычная регрессия на CatBoost, в проде также пробовал LightGBM, разницы по качеству почти нет, CatBoost удобнее с категориальными фичами без предобработки. Модель учится на истории заказов, фичи: сезонность, день недели, цена, цена конкурентов, сток, флаг промо.

Возврат цены в Bitrix идёт через catalog.price.update. Параллельно в отдельную таблицу (Highload-блок) пишу запись: SKU, старая цена, новая цена, дельта в процентах, причина изменения от LLM текстом, прогноз эластичности, timestamp, ID воркфлоу. Эта таблица спасает жизнь, когда через две недели приходит вопрос "почему вчера на X цена прыгнула на 8%". Открываешь, читаешь объяснение от модели, видишь сигналы, принимаешь решение.

Где ставить n8n. Если у вас меньше 2000 активных SKU и нет жёстких требований по приватности данных, берите n8n.cloud, не парьтесь. Self-hosted имеет смысл от 2000 SKU и выше, либо если парсинг конкурентов сложный и нужны кастомные ноды с тяжёлыми зависимостями. Конкретные требования к ресурсам зависят от сложности воркфлоу и объёма каталога. PostgreSQL под n8n лучше вынести отдельно, не в SQLite, иначе на длинных воркфлоу база начинает тупить.

Отдельная боль, rate limit Bitrix API. Из коробки это 2 запроса в секунду, и на каталоге в несколько тысяч позиций вы этот лимит выгребете за минуту. Решение, метод batch, который позволяет упаковать до 50 вызовов в один HTTP-запрос. В n8n я сделал отдельную subworkflow-ноду "Bitrix Batch Caller", которая принимает массив команд, режет на пачки по 50, шлёт и собирает ответы обратно. Прирост скорости выгрузки по сравнению с поштучными запросами весьма существенный.

Архитектурная схема интеграции Bitrix24, n8n и AI-модуля для управления ценами

Три компонента системы работают в связке: Bitrix24 хранит сделки и каталог, n8n оркестрирует потоки данных, AI-модуль принимает ценовые решения.

Сбор данных о конкурентах через n8n: парсеры, API маркетплейсов, прокси

Когда я строю мониторинг цен, первый вопрос, покупать готовый сервис или собирать пайплайн самому. На российском рынке живых игроков несколько: Priceva и Competera для среднего и крупного ритейла, Metacommerce с уклоном в FMCG, Z-Price для небольших B2B-магазинов с прайс-листами в Excel. Если у вас 200-500 SKU и нужна простая матрица "мы vs три конкурента", готовый сервис окупится. Если SKU больше пяти тысяч, есть свои аналитики и хочется кастомной логики ценообразования, дешевле и гибче, n8n плюс Postgres.

Дальше я расскажу, как это собрано у меня.

Источники данных

Для Wildberries и Ozon работают два канала параллельно. Первый, официальный API продавца (Seller API на WB, Performance API и Seller API на Ozon), он отдаёт ваши собственные цены, остатки, заказы. Чужие карточки через него не достать. Второй канал, публичные эндпоинты каталога. У WB это card.wb.ru/cards/v2/detail и catalog.wb.ru/catalog/..., у Ozon публичная страница карточки и внутренний /api/composer-api.bx/page/json/v2. Это те же запросы, которые делает браузер пользователя.

Юридически: цены на публичных страницах магазинов, открытые данные, парсинг их в РФ не запрещён, есть судебная практика в пользу парсеров. Но как только вы обходите капчу, игнорируете robots.txt или льёте больше 1 RPS на один домен с одного IP, появляется риск иска по 1259 ГК (обход технических средств защиты) или жалобы по 152-ФЗ, если случайно зацепили персональные данные продавца. Я держу невысокий RPS на домен и не трогаю то, что закрыто авторизацией.

Частота и приоритезация

Бить все SKU с одинаковой частотой бессмысленно. У меня три кольца:

  • топ-100 SKU (основная выручка), каждые 30 минут
  • средний хвост, около 2000 SKU, каждые 4 часа
  • длинный хвост, раз в сутки, ночью

Такой подход заметно снижает количество запросов без потери качества решений по ценам.

Пайплайн в n8n

Базовая ветка простая: Schedule Trigger → Postgres (читаем список SKU для текущего кольца) → Split In Batches по 50 → HTTP Request к card.wb.ru → Function (нормализация) → Postgres (upsert по паре sku + competitor + ts_bucket).

Ключевой узел, нормализация. WB отдаёт цены в копейках в полях priceU и salePriceU. Уточняйте актуальный состав полей в документации, структура ответа API может меняться. Вот функция, которую я держу в одном Function node:

// n8n Function node: нормализация цены конкурента
const items = $input.all();
return items.map(i => {
  const raw = i.json.priceU || i.json.salePriceU;
  return {
    json: {
      sku: i.json.id,
      competitor: 'wb',
      price: Math.round(raw / 100),
      ts: new Date().toISOString()
    }
  };
});

Дедупликация делается на уровне Postgres: уникальный индекс (sku, competitor, date_trunc('minute', ts)) плюс INSERT ... ON CONFLICT DO NOTHING. Так я не плодю записи, если запрос случайно повторился.

Прокси и антибот

Без прокси один IP рано или поздно ловит 429 от WB. Я использую резидентные пулы Proxy6 и Astroproxy, ротация на каждый запрос. В n8n это решается через credentials типа Generic и поле proxy в HTTP Request. User-Agent ротирую из списка строк (актуальные Chrome 131-134 на Windows и macOS), храню в Postgres-таблице ua_pool, на каждый запрос беру случайный.

Что ещё помогает не получить бан:

  • случайная задержка 200-800 мс между запросами в одном батче (Wait node со случайным значением через expression)
  • заголовки Accept-Language: ru-RU,ru;q=0.9 и Referer на категорию, а не на корень
  • сессии: для Ozon я держу cookie-jar на каждый прокси по 30-40 минут, потом сбрасываю

Если внезапно начинает прилетать капча или 403 на ровном месте, первое подозрение, пул прокси выгорел. Тогда я ротирую подсеть вручную.

Что складываем в Postgres

Минимальная таблица competitor_prices:

sku            text
competitor     text
price          int
old_price      int
in_stock       boolean
seller_id      int
ts             timestamptz

Поверх неё уже строится всё остальное: матрица позиционирования, триггеры на изменение цены конкурента больше чем на 3%, алерты в Telegram через стандартный n8n-нод. Но это уже следующий слой, к парсингу отношения не имеет.

Паук-парсер собирает цены конкурентов с сайтов в автоматическом режиме

Парсер обходит сайты конкурентов по расписанию и складывает актуальные цены прямо в очередь n8n без участия человека.

AI-аналитика: как модель решает, какую цену поставить

Один регрессор на всё это не натянешь. Ситуации слишком разные: демпинг конкурента это одна история, сезонный пик другая, а залежавшийся сток с 90+ днями оборота вообще третья. Поэтому у нас двухэтажная схема. Сначала классификатор (обычный градиентный бустинг на 40 признаках) определяет режим SKU, потом уже специализированная регрессия под каждый режим считает цену. Когда мы смешивали всё в одну модель, она усредняла и тянула цены к медиане рынка, теряя маржу на дефиците и зависая со стоком на распродажах.

Признаки, которые модель реально видит:

  • цены конкурентов: min, median, max, плюс наша дельта к медиане в процентах
  • наша позиция в поисковой выдаче маркетплейса (1-3, 4-10, 11-30, ниже)
  • остатки в днях продаж и абсолютные единицы
  • скорость продаж за 7 и 30 дней, отдельно velocity_ratio = v7/v30 (показывает ускорение или затухание)
  • маржа текущая и пол маржи по категории
  • день недели, флаг праздника, для FMCG ещё температура и осадки по складскому региону

Эластичность считаем по SKU отдельно, на истории промо-акций. Для надёжного расчёта нужна достаточная история продаж и несколько разных ценовых точек, иначе коэффициент будет шумом. Для новинок и хвоста подставляем медианную эластичность по категории, это честнее, чем выдумывать число.

Вызов модели из n8n идёт через Code node, ничего экзотического:

# Псевдокод вызова модели из n8n через Code node
import requests
resp = requests.post('https://ml.internal/price', json={
  'sku': sku,
  'cost': 1240,
  'competitors': [1490, 1520, 1455],
  'stock_days': 47,
  'velocity_7d': 12,
  'margin_floor': 0.18
})
price = resp.json()['recommended_price']

Дальше начинается часть, ради которой мы вообще тащили LLM в пайплайн. Сама регрессия выдаёт число, но категорийный менеджер не примет "1487 рублей, потому что так посчиталось". Поэтому рекомендация и топ-5 фичей по SHAP уходят в LLM, и она пишет текстом: "Снижаем с 1520 до 1487, потому что главный конкурент уронил цену до 1455 три дня назад, а наша позиция в выдаче упала с 4 на 9. Запас 47 дней, риск зависания. Маржа остаётся 19.6%, выше пола категории". Менеджер читает это за 5 секунд и либо подтверждает, либо правит. По нашему опыту, текстовое объяснение рекомендации заметно повышает долю принятых решений по сравнению с голым числом.

Защита от деградации обязательна, иначе модель тихо сожрёт прибыль за квартал и никто не заметит. Хорошая практика: держать часть ассортимента в A/B-тесте, где одна группа работает по модели, другая с зафиксированной ценой (контроль). Раз в неделю смотрим разницу. Если модельная группа стабильно проседает по валовой прибыли, переобучение запускается принудительно, цены откатываются к правилам.

Главная метрика качества именно такая: рост валовой прибыли при сохранении конверсии в разумном коридоре. Просто прибыль ловить опасно, модель быстро учится задирать цены на топах и убивать трафик категории. Просто конверсию ловить тоже бессмысленно, она растёт от любого демпинга. Только связка двух чисел даёт честную картину.

Весы с ценой конкурента и себестоимостью на чашах как символ AI-решения по цене

AI взвешивает цену конкурента, себестоимость и целевую маржу, прежде чем предложить итоговое значение менеджеру.

Правила-ограничители: чтобы AI не уронил бизнес

Любая модель ценообразования рано или поздно попробует продать вам товар себе в убыток. Не потому что она "плохая", а потому что в её функции потерь это локальный максимум: уронить цену, выгрести спрос конкурента, выглядеть красиво в метриках за неделю. Поэтому правила-ограничители я ставлю раньше, чем подключаю саму модель.

Жёсткий пол по марже. Минимальный коэффициент к себестоимости зависит от категории: на низкомаржинальных товарах (электроника) порог ниже, на детских товарах и продовольствии разумно держать его выше. Конкретные коэффициенты определяются экономикой конкретного бизнеса. Сверху потолок: цена не выше RRP производителя более чем на согласованный с вендором процент, иначе ловишь претензии от вендоров и теряешь авторизацию.

Лимит дневного изменения. Резкие скачки цены в 15-20% за сутки вызывают жалобы пользователей и могут трактоваться как недобросовестная практика. Умеренный лимит дневного изменения (порядка 5-10%) позволяет алгоритму реагировать на рынок, не создавая ценовых аттракционов.

Стоп-лист. Туда автоматически попадают:

  • товары в акции (любая активная промо-кампания блокирует AI до её окончания)
  • позиции под MAP-соглашением с производителем (Minimum Advertised Price, нарушать нельзя)
  • SKU из госконтрактов, где цена зафиксирована протоколом
  • новинки на первое время после ввода в каталог, пока нет статистики спроса

Защита от ценовых войн. Если несколько конкурентов из моей референсной корзины синхронно падают больше чем на 5% за сутки, система не следует за ними. Она ставит цену на hold и пишет в Telegram-канал категорийному менеджеру. В большинстве случаев это либо распродажа склада у конкурента, либо ошибка в их парсере. Бежать туда деньгами не нужно.

Требования к документированию цен. Цена на карточке товара и цена в корзине должны совпадать на момент оформления заказа. Если AI пересчитал цену между кликом "в корзину" и оформлением заказа, действует старая. Технически это блокировка пересчёта на корзинных SKU плюс лог всех изменений с timestamp. Историю изменений цен я храню, чтобы по запросу регулятора или в случае спора с покупателем можно было отдать выгрузку.

Ручной override. В админке Bitrix у категорийщика есть кнопка "зафиксировать на N дней" с дропдауном 1/3/7/14/30. Нажал, ввёл цену, AI на этот SKU молчит до истечения срока. Использую сам, когда вижу что модель залипла в локальном минимуме или когда маркетинг готовит запуск и не хочет сюрпризов. Если override стоит на слишком малой доле ассортимента, это может означать, что менеджеры просто не пользуются инструментом.

Все эти правила работают как hard constraints поверх рекомендаций модели: AI предлагает цену, валидатор прогоняет её через проверки выше, и только потом цена улетает в каталог. Если хоть одна проверка падает, цена откатывается на предыдущую и в лог пишется причина. Раз в неделю смотрю, какие constraints срабатывают чаще всего, иногда это сигнал, что в модели поехала калибровка.

Светофор с зелёным, жёлтым и красным уровнями допустимого снижения цены

Система ограничений работает как светофор: зелёная зона разрешает автоматическое изменение, красная блокирует цену и требует ручного одобрения.

Пошаговая сборка workflow в n8n

Этот пайплайн я собирал под топ-SKU одного fashion-ритейлера. Решение рабочее: крутится в проде, в среднем перерасчитывает десятки-сотни цен за прогон, тяжёлые операции вынесены в очередь.

Триггер. Schedule Trigger с интервалом 30 минут для топовых позиций. Для не-топовых SKU отдельный воркфлоу с шагом в 6 часов, фильтр по полю topSeller=Y.

Шаг 1. Получение активных SKU из Bitrix. HTTP Request POST на catalog.product.list с фильтром по остатку >0 и активности. REST Bitrix отдаёт максимум 50 элементов за запрос, так что использую n8n-овский pagination в самом ноде (Response → Pagination → Update a Parameter in Each Request). Возвращаю массив с полями id, iblockId, name, currentPrice, purchasePrice, stock, barcode.

Шаг 2. Парсинг конкурентов. Здесь начинается самое нервное. Split In Batches с небольшим размером (много параллельных запросов к WB быстро приводят к бану по IP), затем ветки:

  • WB через https://card.wb.ru/cards/v2/detail по nmId, маппинг хранится в Postgres
  • Ozon через их Seller API, авторизация по Client-Id/Api-Key
  • Яндекс.Маркет через partner-api (уточняйте актуальные лимиты в документации)
  • опционально Lamoda и Wildberries Premium для люкса

Каждая ветка возвращает {sku_id, source, competitor_price, in_stock, fetched_at}. Обязательно ставлю Continue On Fail: если WB лёг, мы не теряем весь прогон.

Шаг 3. Агрегация в Postgres. Узел Postgres (insert), таблица competitor_prices партицирована по дате. Дальше отдельным SELECT подтягиваю окно за последние 4 часа и считаю фичи: медиана, min, max, std, count_sources, доля источников с наличием. Расчёт делаю в SQL, не в Code node, потому что на большом числе SKU и 4-часовом окне тысячи строк, и JS их жуёт ощутимо медленнее.

SELECT sku_id,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY competitor_price) AS median_p,
       MIN(competitor_price) AS min_p,
       MAX(competitor_price) AS max_p,
       STDDEV(competitor_price) AS std_p,
       COUNT(DISTINCT source) AS sources_cnt
FROM competitor_prices
WHERE fetched_at > NOW() - INTERVAL '4 hours'
GROUP BY sku_id;

Шаг 4. ML или LLM. У меня два режима. Если SKU попадает в группу с достаточной историей продаж, гоню в собственный CatBoost-эндпоинт (FastAPI на отдельной VM), он отвечает быстро. Если истории нет или товар в новой категории, подключаю Claude Sonnet 4.5 через Anthropic API с system prompt вида:

Ты ассистент категорийного менеджера. На входе JSON с текущей ценой, закупкой, остатком, медианой/мин/макс конкурентов, маржой за 30 дней. Верни JSON {price, reason, confidence}. Правила: маржа не ниже 18%, цена не выше max конкурентов +3%, не ниже min конкурентов -2%, шаг округления 10 руб для цен >1000, иначе 1 руб.

Рекомендации с низким confidence отправляем в ручную модерацию, не применяем.

Шаг 5. Валидация в Code node. Здесь отлавливаю всё, что модели всё равно умудряются выдать. JS-узел:

const items = $input.all();
const out = [];
for (const it of items) {
  const d = it.json;
  const newP = d.recommended_price;
  const cur = d.currentPrice;
  const purch = d.purchasePrice;

  // отбрасываем выбросы
  if (!newP || newP <= 0) continue;
  if (newP < purch * 1.18) continue;            // маржа
  if (Math.abs(newP - cur) / cur > 0.25) continue; // скачок >25%
  if (d.sources_cnt < 2) continue;              // мало данных

  out.push({ json: { ...d, new_price: newP, delta_pct: (newP - cur) / cur } });
}
return out;

25% сверху, 18% снизу по марже, минимум 2 источника.

Пошаговый пайплайн n8n-воркфлоу с узлами парсинга, расчёта и обновления цены

Воркфлоу из семи узлов проходит путь от получения данных с сайта конкурента до записи новой цены в карточку товара Bitrix24.