Мультикарточный портфель — аудит ВСЕХ SKU клиента
Как провести полный аудит портфеля продавца, рассчитать потенциал ROI для каждой карточки и сформировать пакетное предложение, которое в 4-6 раз выше обычного чека за одну карточку.
Проблема
У большинства продавцов на WB/Ozon десятки или сотни SKU. Когда такой селлер обращается в Fotofactor, он просит обновить одну карточку. Какую? Ту, которая ему нравится больше всего, или ту, что и так хорошо продаётся. Но это ошибка приоритизации:
- «Обновите мой бестселлер» — у бестселлера и так хорошие фото, 12 картинок и видео. Улучшение даст +5% конверсии
- «Этот товар мне нравится» — но он приносит 3% выручки. Даже идеальный контент не изменит картину
- «Самый дорогой товар» — высокая цена != высокий потенциал ROI на контент
Максимальный ROI от контент-обновления получает карточка с наибольшим разрывом между текущим контентом и потенциалом продаж. Это карточка с высокой выручкой, но плохими фото. Или с огромной упущенной выручкой (lost_profit), которую можно вернуть хорошим контентом.
Без анализа всего портфеля селлер тратит 80 000 рублей на обновление не той карточки. А карточка с потенциалом ROI x10 лежит забытая на 5-м экране кабинета.
73% селлеров обновляют контент «по ощущениям» — выбирают карточку интуитивно. В результате:
- 40% бюджета уходит на карточки, которые и так работают нормально (ROI < 1.5x)
- Карточки с потенциалом ROI 5-10x остаются без внимания годами
- Селлер не видит результата → разочаровывается в контент-услугах → уходит
Мультикарточный аудит решает эту проблему: мы показываем где именно каждый рубль даст максимальную отдачу.
Пайплайн данных
graph LR
A["MPStats API\nВсе SKU продавца"] --> B["Скоринг каждого SKU\ncontent_gap x revenue"]
B --> C["Ранжирование\nпо потенциалу ROI"]
C --> D["Pareto-анализ\n20% SKU = 80% эффекта"]
D --> E["Портфельная матрица\nс приоритетами"]
E --> F["Пакетное предложение\nТОП-10 с дисконтом"]
Источники данных
| Эндпоинт | Метод | Ключевые поля | Зачем |
|---|---|---|---|
POST /wb/get/category | POST | id, revenue, sales, picscount, hasvideo, lost_profit, rating, comments, category_position | Получить ВСЕ товары конкретного продавца (фильтр по supplier_id) |
GET /wb/get/item/{sku}/by_keywords | GET | keyword, position, frequency | SEO-профиль для ТОП-SKU (опционально, для детального анализа) |
MPStats не имеет отдельного эндпоинта «все товары продавца». Вместо этого используем POST /wb/get/category с фильтром filterModel по supplier_id. Запрос возвращает все товары в выбранной категории для конкретного поставщика. Если селлер работает в нескольких категориях — делаем несколько запросов и объединяем результаты.
Альтернатива: если у селлера публичный магазин на WB — можно собрать все SKU через страницу продавца и затем обогатить дан ными из MPStats.
Ключевые поля для скоринга
| Поле | Тип | Для чего |
|---|---|---|
supplier_id | string | Фильтр — выбрать только товары конкретного продавца |
revenue | number | Текущая выручка — вес приоритета (больше выручка = важнее карточка) |
lost_profit | number | Упущенная выручка — индикатор потенциала роста |
picscount | number | Количество фото — основной gap-индикатор контента |
hasvideo | boolean | Наличие видео — сильный gap-индикатор |
category_position | number | Позиция в категории — room for improvement |
rating | number | Рейтинг — коррелирует с качеством контента |
comments | number | Количество отзывов — социальное доказательство |
Анализ
Шаг 1. Загрузка всех SKU продавца
import httpx
from urllib.parse import quote
MPSTATS_TOKEN = "YOUR_TOKEN"
BASE_URL = "https://mpstats.io/api"
HEADERS = {
"X-Mpstats-TOKEN": MPSTATS_TOKEN,
"Content-Type": "application/json",
}
def fetch_seller_products(
category: str, supplier_id: str, d1: str, d2: str
) -> list:
"""
Загружает все товары конкретного продавца в категории.
Если продавец работает в нескольких категориях —
вызвать для каждой и объединить результаты.
"""
all_products = []
start_row = 0
batch_size = 100
while True:
resp = httpx.post(
f"{BASE_URL}/wb/get/category",
headers=HEADERS,
json={
"path": quote(category, safe=""),
"d1": d1,
"d2": d2,
"startRow": start_row,
"endRow": start_row + batch_size,
"filterModel": {
"supplier_id": {
"filterType": "text",
"type": "equals",
"filter": supplier_id,
}
},
"sortModel": [{"colId": "revenue", "sort": "desc"}],
},
timeout=30,
)
resp.raise_for_status()
data = resp.json().get("data", [])
if not data:
break
all_products.extend(data)
start_row += batch_size
if len(data) < batch_size:
break
return all_products
# --- Загрузка ---
categories = [
"Красота/Ароматы для дома",
"Красота/Аксессуары для ароматерапии",
]
supplier_id = "ООО Ароматный мир" # Имя поставщика из MPStats
all_skus = []
for cat in categories:
products = fetch_seller_products(cat, supplier_id, "2025-01-01", "2025-01-31")
all_skus.extend(products)
print(f"Категория '{cat}': {len(products)} SKU")
print(f"\nВсего SKU продавца: {len(all_skus)}")
Шаг 2. Скоринг каждого SKU по потенциалу ROI
Для каждой карточки рассчитываем improvement_potential — комплексный балл, учитывающий:
- Насколько плох текущий контент (content_gap)
- Насколько велика выручка (revenue_weight)
- Насколько велика упущенная выручка (lost_profit_ratio)
def calculate_portfolio_scores(products: list, benchmark_photos: int = 12) -> list:
"""
Рассчитывает потенциал ROI для каждого SKU.
Формула:
content_gap = (benchmark_photos - picscount) + (1 - hasvideo) * 20
revenue_weight = revenue / max_revenue
lost_profit_ratio = lost_profit / (revenue + 1)
improvement_potential = content_gap * revenue_weight * (1 + lost_profit_ratio)
Чем выше improvement_potential — тем больше ROI от обновления контента.
"""
if not products:
return []
max_revenue = max(p.get("revenue", 0) for p in products) or 1
scored = []
for p in products:
revenue = p.get("revenue", 0)
lost_profit = p.get("lost_profit", 0)
picscount = p.get("picscount", 0)
hasvideo = p.get("hasvideo", 0)
rating = p.get("rating", 0)
position = p.get("category_position", 999)
# Контент-разрыв: сколько "единиц контента" не хватает
photo_gap = max(0, benchmark_photos - picscount)
video_gap = (1 - hasvideo) * 20 # Отсутствие видео = 20 баллов gap
content_gap = photo_gap + video_gap
# Весовой коэффициент выручки (0..1)
revenue_weight = revenue / max_revenue
# Коэффициент упущенной выручки
lost_profit_ratio = lost_profit / (revenue + 1)
# Итоговый потенциал
potential = round(
content_gap * revenue_weight * (1 + lost_profit_ratio), 1
)
# Классификация приоритета
if potential >= 60:
priority = "urgent"
priority_label = "Срочно"
emoji = "🔴"
elif potential >= 30:
priority = "high"
priority_label = "Высокий"
emoji = "🟡"
elif potential >= 10:
priority = "medium"
priority_label = "Средний"
emoji = "🟢"
else:
priority = "low"
priority_label = "Низкий"
emoji = "⚪"
scored.append({
"sku": p["id"],
"name": p.get("name", ""),
"revenue": revenue,
"lost_profit": lost_profit,
"picscount": picscount,
"hasvideo": hasvideo,
"rating": rating,
"position": position,
"content_gap": content_gap,
"revenue_weight": round(revenue_weight, 3),
"lost_profit_ratio": round(lost_profit_ratio, 3),
"improvement_potential": potential,
"priority": priority,
"priority_label": priority_label,
"emoji": emoji,
})
# Сортировка: наибольший потенциал наверху
scored.sort(key=lambda x: x["improvement_potential"], reverse=True)
# Нумерация
for i, item in enumerate(scored, 1):
item["rank"] = i
return scored
# --- Скоринг ---
portfolio = calculate_portfolio_scores(all_skus)
print("=== ТОП-10 SKU ПО ПОТЕНЦИАЛУ ROI ===\n")
for item in portfolio[:10]:
print(
f" #{item['rank']} {item['emoji']} SKU {item['sku']}"
f" | Выручка: {item['revenue']:,.0f} руб"
f" | Фото: {item['picscount']}"
f" | Видео: {'Да' if item['hasvideo'] else 'Нет'}"
f" | Lost Profit: {item['lost_profit']:,.0f} руб"
f" | Потенциал: {item['improvement_potential']}"
)
Шаг 3. Pareto-анализ портфеля
def pareto_analysis(scored_portfolio: list) -> dict:
"""
Pareto-анализ: какие 20% SKU дают 80% потенциала.
Помогает определить минимальный пакет обновлений
для максимального эффекта.
"""
total_potential = sum(s["improvement_potential"] for s in scored_portfolio)
total_revenue = sum(s["revenue"] for s in scored_portfolio)
cumulative = 0
pareto_threshold = total_potential * 0.8
pareto_skus = []
for item in scored_portfolio:
cumulative += item["improvement_potential"]
pareto_skus.append(item)
if cumulative >= pareto_threshold:
break
return {
"total_skus": len(scored_portfolio),
"pareto_skus": len(pareto_skus),
"pareto_pct": round(len(pareto_skus) / len(scored_portfolio) * 100, 1),
"total_potential": round(total_potential, 1),
"pareto_potential": round(cumulative, 1),
"total_revenue": total_revenue,
"pareto_revenue": sum(s["revenue"] for s in pareto_skus),
"items": pareto_skus,
}
pareto = pareto_analysis(portfolio)
print(f"=== PARETO-АНАЛИЗ ===")
print(f"Всего SKU: {pareto['total_skus']}")
print(f"SKU для обновления (80% эффекта): {pareto['pareto_skus']} "
f"({pareto['pareto_pct']}%)")
print(f"Суммарная выручка этих SKU: {pareto['pareto_revenue']:,.0f} руб")
Портфельная матрица
Результат анализа для реального клиента (производитель ароматов для до ма, 47 SKU):
| # | SKU | Название | Выручка | Фото | Видео | Lost Profit | Потенциал | Приоритет |
|---|---|---|---|---|---|---|---|---|
| 1 | 183749201 | Набор аромат. свечей «Лаванда» | 1.2M | 4 | Нет | 300K | 92 | 🔴 Срочно |
| 2 | 183749215 | Автоосвежитель «Хвоя» | 800K | 6 | Нет | 150K | 71 | 🟡 Высокий |
| 3 | 183749228 | Аромадиффузор палочки | 650K | 5 | Нет | 180K | 65 | 🟡 Высокий |
| 4 | 183749234 | Саше для белья | 420K | 3 | Нет | 90K | 58 | 🟡 Высокий |
| 5 | 183749240 | Набор эфирных масел | 380K | 7 | Нет | 70K | 42 | 🟡 Высокий |
| 6 | 183749251 | Аромалампа керамика | 290K | 8 | Нет | 45K | 31 | 🟡 Высокий |
| 7 | 183749267 | Спрей для дома «Цитрус» | 210K | 6 | Нет | 35K | 24 | 🟢 Средний |
| 8 | 183749273 | Свеча соевая «Ваниль» | 180K | 9 | Нет | 20K | 18 | 🟢 Средний |
| 9 | 183749285 | Ароматизатор в машину | 150K | 5 | Нет | 25K | 16 | 🟢 Средний |
| 10 | 183749290 | Диффузор «Морской бриз» | 2.1M | 12 | Да | 50K | 35 | 🟢 Низкий |
| ... | ... | ... остальные 37 SKU ... | ... | ... | ... | ... | <10 | ⚪ Низкий |
Pareto-результат: 6 из 47 SKU (13%) покрывают 80% потенциала ROI.
Дерево решений
graph TD
A["Получить все SKU\nпродавца через MPStats"] --> B["Рассчитать content_gap\nдля каждого SKU"]
B --> C["Рассчитать revenue_weight\n+ lost_profit_ratio"]
C --> D["improvement_potential =\ncontent_gap x revenue_weight\nx (1 + lost_profit_ratio)"]
D --> E{"Потенциал SKU?"}
E -->|">= 60 (Срочно)"| F["Фаза 1\nНемедленное обновление\nПолная пересъёмка"]
E -->|"30-59 (Высокий)"| G["Фаза 2\nОбновление в течение\n1-2 месяцев"]
E -->|"10-29 (Средний)"| H["Фаза 3\nОбновление при\nналичии ресурсов"]
E -->|"< 10 (Низкий)"| I["Пропустить\nROI не оправдывает\nзатрат"]
F --> J["Пакетное предложение\nТОП-10 со скидкой -30%"]
G --> J
H --> K["Отдельное предложение\nпо запросу"]
Действие Fotofactor
Формирование пакетного предложения
На осно ве портфельного аудита Fotofactor предлагает клиенту пакетное обновление вместо одиночных карточек:
- Портфельный отчёт — PDF на 10-15 страниц с полной матрицей SKU, потенциалом ROI и Pareto-анализом
- Пакет ТОП-10 — обновление 10 приоритетных карточек с дисконтом за объём
- Фазовый подход — разбивка на 3 фазы для управления бюджетом
Пакет «Портфель ТОП-10» — обновление 10 приоритетных карточек:
Поштучная цена: 10 x 80 000 рублей = 800 000 рублей
Пакетная цена: 560 000 рублей (скидка 30%)
Разбивка по фазам:
- Фаза 1 (ТОП-5, срочные): 5 карточек = 250 000 рублей — с тарт немедленно
- Фаза 2 (следующие 5): 5 карточек = 220 000 рублей — через 3-4 недели
- Фаза 3 (остальные по запросу): индивидуально, 70 000 рублей/карточка
Почему скидка 30% выгодна Fotofactor:
- Объём: 10 карточек одного клиента дешевле в продакшне, чем 10 клиентов по 1 карточке (единый стиль, один бренд, одна съёмка)
- Cash flow: предоплата 50% = 280 000 рублей за раз
- Retention: клиент «привязан» на 2-3 месяца проекта
- Upsell: после фазы 1 показываем ROI — фаза 2 утверждается автоматически
Предпродажная коммуникация
Шаблон outreach для продавца:
Добрый день! Провели портфельный аудит ваших карточек на WB.
📊 Результаты анализа 47 SKU:
- 6 карточек с критическим разрывом контента (потенциал ROI > x5)
- Суммарная упущенная выручка: ~890 000 руб/мес
- У 38 из 47 карточек нет видео (81%)
- Среднее количество фото: 6.2 (benchmark категории: 12)
🔥 ТОП-3 по приоритету:
1. "Набор аромат. свечей" — 1.2M выручка, всего 4 фото, нет видео
2. "Автоосвежитель Хвоя" — 800K выручка, 6 фото, нет видео
3. "Аромадиффузор палочки" — 650K выручка, 5 фото, нет видео
💡 Предлагаем: пак етное обновление ТОП-10 карточек
— Скидка 30% при пакетном заказе (560K вместо 800K)
— Фазовый подход: сначала 5 самых приоритетных
— После Фазы 1 — покажем ROI и согласуем Фазу 2
📅 Подготовили полный портфельный отчёт с матрицей приоритетов — отправить?
Экономическое обоснование для клиента
Для каждой карточки из ТОП-10 рассчитываем прогнозируемый ROI:
def project_roi(scored_item: dict, update_cost: float = 56_000) -> dict:
"""
Прогнозируемый ROI от обновления контента карточки.
update_cost: стоимость обновления одной карточки в пакете
(после скидки 30%: 80K * 0.7 = 56K)
Допущения:
- Улучшение контента возвращает 30-50% lost_profit
- Новое видео добавляет +10-15% к конверсии
- Оптимальное кол-во фото: +5-8% к конверсии за каждое
"""
lost_profit = scored_item["lost_profit"]
has_video = scored_item["hasvideo"]
picscount = scored_item["picscount"]
# Прогноз дополнительной выручки в месяц
recovered_lost = lost_profit * 0.35 # 35% от lost_profit
video_uplift = scored_item["revenue"] * 0.12 if not has_video else 0
photo_uplift = scored_item["revenue"] * 0.05 * max(0, 12 - picscount) / 12
monthly_uplift = recovered_lost + video_uplift + photo_uplift
annual_uplift = monthly_uplift * 12
roi = annual_uplift / update_cost if update_cost > 0 else 0
return {
"sku": scored_item["sku"],
"update_cost": update_cost,
"monthly_uplift": round(monthly_uplift),
"annual_uplift": round(annual_uplift),
"roi_x": round(roi, 1),
"payback_months": round(update_cost / monthly_uplift, 1) if monthly_uplift > 0 else 99,
}
# --- Прогноз ROI для ТОП-10 ---
print("=== ПРОГНОЗ ROI ПО ПАКЕТУ ТОП-10 ===\n")
total_cost = 0
total_annual_uplift = 0
for item in portfolio[:10]:
roi = project_roi(item)
total_cost += roi["update_cost"]
total_annual_uplift += roi["annual_uplift"]
print(
f" SKU {roi['sku']}: "
f"затраты {roi['update_cost']:,.0f} руб → "
f"+{roi['monthly_uplift']:,.0f} руб/мес → "
f"ROI {roi['roi_x']}x за год, "
f"окупаемость {roi['payback_months']} мес"
)
print(f"\nИТОГО ПАКЕТ:")
print(f" Затраты: {total_cost:,.0f} руб")
print(f" Доп. выручка: +{total_annual_uplift:,.0f} руб/год")
print(f" ROI пакета: {total_annual_uplift / total_cost:.1f}x")
Результат для клиента
Что получает клиент
- Data-driven приоритизация — не «обновите любимую карточку», а «вот 6 карточек, где каждый вложенный рубль даёт максимальную отдачу»
- Экономия на пакете — 30% скидка при обновлении ТОП-10 вместо поштучного заказа
- Прогноз ROI по каждому SKU — конкретные цифры: «вложили 56 000 рублей → получили +105 000 рублей/мес»
- Фазовый подход — не нужно тратить 560 000 рублей сразу, можно начать с Фазы 1 за 250 000 рублей и увидеть результат
- Портфельная стратегия — план обновления всех карточек на 3-6 месяцев вперёд
Эффект пакетного подхода
| Метрика | Одна карточка | Пакет ТОП-10 | Разница |
|---|---|---|---|
| Стоимость для клиента | 80 000 руб | 560 000 руб (56 000/шт) | Скидка 30% |
| Количество обновлений | 1 SKU | 10 SKU | x10 охват |
| Прогнозируемый uplift | +50 000-100 000 руб/мес | +400 000-800 000 руб/мес | x5-10 эффект |
| Окупаемость | 1-2 месяца | 1-2 месяца | Одинаково |
| Привязка клиента | Разовый заказ | 2-3 месяца проект | Retention |
Экономика для Fotofactor
| Метрика | Без портфельного аудита | С портфельным аудитом |
|---|---|---|
| Средний чек | 80 000 руб (1 карточка) | 300 000-560 000 руб (пакет) |
| Конверсия из лида | 20-30% | 50-65% (аудит продаёт сам себя) |
| LTV клиента | 80 000-160 000 руб | 700 000-1 200 000 руб (3 фазы + мониторинг) |
| Маржинальность | 40-50% | 55-65% (экономия на продакшне пакета) |
| Загрузка студии | Непредсказуемая | Плановая (проект на 2-3 месяца) |
Формула improvement_potential = content_gap x revenue_weight x (1 + lost_profit_ratio) учитывает три фактора:
- content_gap — насколько плох текущий контент. Карточка с 4 фото и без видео получает высокий gap. Карточка с 12 фото и видео — низкий.
- revenue_weight — нормализованная выручка. Карточка с 1.2M руб/мес важнее, чем карточка с 50K руб/мес, даже если у обеих одинаковый content_gap.
- lost_profit_ratio — доля упущенной выручки. Если MPStats показывает lost_profit = 300K при revenue = 1.2M, это 25% упущенных продаж — серьёзный сигнал.
Произведение трёх факторов выделяет карточки, где одновременно плохой контент, высокая выручка и большой потенциал роста. Это именно те карточки, где обновление контента даст максимальный ROI.
Воронка мультикарточного аудита
flowchart TD
A["Клиент обращается:\n«Обновите одну карточку»"] --> B["Fotofactor: «Давайте сначала\nпроверим весь портфель.\nАудит — бесплатно»"]
B --> C["Загрузка всех SKU\nчерез MPStats API"]
C --> D["Скоринг + Pareto-анализ\n20 минут автоматизации"]
D --> E["PDF-отчёт с матрицей\nприоритетов по ROI"]
E --> F["Презентация клиенту:\n«Вы хотели обновить\nне ту карточку»"]
F --> G{"Реакция клиента?"}
G -->|"«Вау, дайте пакет»\n(50-65%)"| H["Пакет ТОП-10\n560 000 руб"]
G -->|"«Начнём с ТОП-5»\n(20-25%)"| I["Фаза 1\n250 000 руб"]
G -->|"«Пока одну»\n(10-15%)"| J["1 карточка\n80 000 руб\n+ follow-up через\n1 месяц с ROI"]
I --> K["Показать ROI\nпосле Фазы 1"]
K --> L["Фаза 2 утверждается\nавтоматически (85%)"]
J --> M["Follow-up:\nROI одной карточки\n→ Upsell на пакет"]
Unit-экономика
Вход: Имя продавца + категория (1-3 API-запроса)
↓
Анализ: Скоринг 47 SKU + Pareto (~20 минут)
↓
Отчёт: PDF 10-15 страниц с матрицей приоритетов
↓
Пакет ТОП-10: 560 000 руб (скидка 30%)
↓
Фаза 1 (ТОП-5): 250 000 руб → показать ROI
Фаза 2 (ТОП 6-10): 220 000 руб → утверждение 85%
Фаза 3 (остальные): по запросу
↓
Мониторинг: 25 000 руб/мес (ежемесячный отчёт)
↓
LTV: 700 000 - 1 200 000 руб/год на клиента
Что дальше
- Конкурентный рентген -- глубокий анализ конкурентов для каждой приоритетной карточки из портфеля
- Сезонный контент-календарь -- годовой план обновлений с учётом сезонности по каждому SKU
- Контент-усталость -- детектор падения конверсии для мониторинга обновлённых карточек
- Промо-готовность -- подготовка контента к акциям и распродажам WB
- Примеры API-запросов -- готовые сниппеты для работы с MPStats API