API для организаций: выгрузка намерений
Организации загружают в «ХочуМогу» каталог товаров пакетно — по ссылке на файл или прямым вызовом API, без ручного ввода через мастер. Конвейер сам превращает позиции каталога в намерения «Могу продать». Оба способа уже работают и доступны в личном кабинете организации: блок «Импорт по ссылке» (поле URL, автосинхронизация, кнопка «Проверить сейчас») и «Подключение по API» (выпуск токена доступа).
1. Аутентификация
Прямой вызов авторизуется персональным токеном организации в заголовке Authorization: Bearer <org_token>. Токен выдаётся в личном кабинете организации (блок «Подключение по API» → «Выпустить ключ»), показывается один раз в момент выпуска и привязан к аккаунту — загруженные товары становятся намерениями от его имени. Для импорта «по ссылке» токен не нужен: фидом управляет сама организация под обычной сессией в личном кабинете.
2. Два способа загрузки
Оба принимают один и тот же формат файла (раздел 3 ниже).
- Прямой вызов (push). Один запрос — один прогон синхронизации:
POST /v1/org/intentions/importс телом-файлом. - По ссылке (pull). Организация один раз указывает URL файла в блоке «Импорт по ссылке» личного кабинета; сервис сам периодически его забирает и синхронизирует (подробности — раздел 6).
POST /v1/org/intentions/import?dry_run=1
Authorization: Bearer nmr_org_<32 hex-символа>
Content-Type: application/xml
# тело — сам XML-файл целиком, БЕЗ JSON-обёртки (см. формат ниже)
В счётчиках отчёта есть no_photo — сколько товаров пришло без единой картинки. Такой товар публикуется как обычно, просто без фотографии: положить её можем только вы, добавив <picture> в выгрузку. Товары, у которых ссылка на картинку есть, но не открывается, в это число не входят.
Там же desc_truncated — у скольких товаров описание пришлось сократить: описание намерения вмещает 6000 символов вместе с названием товара. Товар при этом публикуется, сокращение идёт по границе абзаца или строки.
dry_run=1 — сухой прогон: тот же отчёт, что и боевой импорт, но без создания и изменения намерений (пригодится, чтобы проверить файл перед первой загрузкой). remove_missing=1 — закрыть намерения, чьи товары пропали из текущего файла (без параметра пропавшие товары остаются как есть).
3. Формат файла: YML Яндекс.Маркета
Сервис принимает один формат — YML Яндекс.Маркета (XML), тот самый файл, который у вас, скорее всего, уже есть: его выгружают 1С-Битрикс, InSales и другие системы, обычно по адресу вида /bitrix/catalog_export/yandex_НОМЕР.php. Ничего специально готовить не нужно — дайте ссылку на существующую выгрузку либо проверьте файл сухим прогоном перед загрузкой. Формат определяется по содержимому файла, расширение ссылки и заголовок Content-Type значения не имеют; файл в любом другом формате сервис не примет (раздел 5, код 422).
Что сервис берёт из YML: название товара, описание <description>, цену, категорию (путь по дереву <categories> собирается автоматически по <categoryId>), производителя <vendor>, характеристики <param> и изображения <picture> — картинки скачиваются к нам и показываются на карточке намерения (ссылки на ваш сайт мы не сохраняем: они перестанут работать, если вы переставите файлы). Берётся несколько первых картинок товара — сколько именно, настраивает оператор сервиса, по умолчанию три. Перенос идёт фоном после импорта, поэтому фотографии появляются на несколько минут позже самих намерений. Если какие-то картинки не доехали (ваш сервер был недоступен, ссылка временно не работала), сервис доберёт их при следующем прогоне фида — заново загружать выгрузку не нужно. Одна и та же картинка, поставленная у нескольких товаров, хранится у нас один раз и достаётся всем им. Товары с available="false" пропускаются — намерение «могу продать» о том, чего нет на складе, никому не поможет. Кодировка windows-1251, в которой Битрикс выгружает по умолчанию, распознаётся сама.
Файл — список товаров вашего каталога, а не готовых намерений: сервис сам решает, во что превратить каждый товар. Как поле обязателен только атрибут id у <offer> — без него запись не свяжется с намерением, сервис вернёт по товару ошибку и не создаст черновик. <categoryId> тоже нужен практически всегда: без категории товар не пропадает с ошибкой всего запроса, но пропускается (раздел 5). Дальше — не поля, а требования к результату: импорт всегда создаёт намерения «Могу продать», а для этого типа по умолчанию обязательны цена и хотя бы один тег, который узнает словарь характеристик — оба разобраны в подсказках под примером.
<?xml version="1.0" encoding="utf-8"?>
<yml_catalog>
<shop>
<name>Мой магазин</name>
<categories>
<category id="40">Инструменты</category>
<category id="50" parentId="40">Электроинструмент</category>
<category id="60" parentId="50">Дрели-шуруповёрты</category>
<category id="10">Женщинам</category>
<category id="20" parentId="10">Женская одежда</category>
<category id="30" parentId="20">Платья</category>
</categories>
<offers>
<offer id="sku-101">
<name>Дрель-шуруповёрт Bosch GSR 12V-15, 2 АКБ, кейс</name>
<description><![CDATA[<p>Компактная дрель для работы <b>в ограниченном пространстве</b>.</p>
<h3>В комплекте</h3>
<ul><li>два аккумулятора</li><li>зарядное устройство</li><li>кейс</li></ul>]]></description>
<categoryId>60</categoryId>
<vendor>Bosch</vendor>
<price>8990</price>
<param name="Тип аккумулятора">Li-Ion</param>
<param name="Наличие реверса">да</param>
<param name="Цвет">синий</param>
</offer>
<offer id="sku-102">
<name>Платье летнее хлопковое, синее, 44 размер</name>
<categoryId>30</categoryId>
<vendor>Zarina</vendor>
<price>2490</price>
<param name="Размер">44</param>
<param name="Цвет">синий</param>
</offer>
</offers>
</shop>
</yml_catalog>
- id (атрибут
<offer id="…">) — ваш идентификатор товара (он жеexternal_id), обязателен: без него сервис не создаст запись, а вернёт по этому товару ошибку «у товара нет id (в YML обязателен атрибут <offer id>)» вerrorsотчёта (раздел 5) — в намерение он не превратится. - categoryId — ссылка на категорию из дерева
<categories>; путь сервис собирает сам по вложенностиparentId. Категория — источник тегов и запасное название намерения на случай, когда из названия товара взять нечего: знакомую сервису категорию он переводит по словарю (лист «Дрели-шуруповёрты» → «дрель-шуруповёрт»), незнакомую — берёт по её имени. Товар без категории или с пустым именем категории пропускается (попадает вskippedотчёта, раздел 5, с причиной «пустая категория»). - name — обычное название товара из вашего каталога. Из него сервис берёт НАЗВАНИЕ намерения — одно-два значимых слова, то, что покупатель наберёт в своём «хочу купить» («Дрель-шуруповёрт Bosch GSR 12V-15…» → «дрель-шуруповёрт»). Полное название целиком становится первым абзацем описания намерения: модель и размер нужны покупателю, а в коротком названии намерения им нет места.
- description — описание товара, необязательно. Оно идёт в описание намерения следом за названием. Годится и обычный текст, и HTML — внутри
<![CDATA[ … ]]>, экранированный или просто тегами. Сохраняется оформление: абзацы и переносы, подзаголовки (<h1>–<h6>— одного уровня), списки, жирный и курсив; строки, начатые «- » или «• », тоже становятся пунктами списка. Ссылки остаются текстом без адреса, стили, цвета и размеры шрифта отбрасываются, картинки и скрипты удаляются целиком. Если описание уже начинается с названия товара, название второй раз не добавляется. Описание намерения вмещает 6000 символов — длиннее сокращается по границе абзаца, и это видно по счётчикуdesc_truncated(раздел 2). - vendor и param (характеристика в теге
<param name="…">…</param>) — становятся тегами намерения по внутреннему словарю, но только при ТОЧНОМ (без учёта регистра) совпадении имени характеристики со словарём: например, «Наличие реверса: да» → тег «с реверсом», а вот просто «Реверс: да» словарь не узнает по имени и молча отбросит. У каждого узнанного тега есть вес важности (зависит от категории товара, домена и самой характеристики), и в намерение он попадает, только если вес не ниже порога отсечения — например, «Цвет» у инструментов намеренно снижен (для подбора совпадений по дрели цвет не важен) и почти всегда отбрасывается, а у одежды, наоборот, входит в вес. В примере выше поэтому «Наличие реверса: да» у первого товара становится тегом «с реверсом», а «Цвет: синий» у него же — отбрасывается по весу; у второго товара (одежда) «Цвет: синий» тегом становится. Тегов на намерение остаётся немного (алгоритм подбора совпадений по тегам точнее работает с коротким набором самых важных, а не длинным списком характеристик). Отброшенное — не ошибка: агрегированную статистику (сколько раз и по какой причине) показывает полеmetrics.drop_reasonsотчёта (раздел 5); привязки «какой именно товар и характеристика» в отчёте нет — это сводка по всему фиду. - Если характеристик (
<param>) нет вовсе — а в выгрузках магазинов так чаще всего и бывает, — теги собираются из НАЗВАНИЯ товара: значимые слова, латинская марка и размеры. Размер понимается в трёх привычных формах:8х 60→ тег8х60,М 14,0→м14,d 3иДу 20→d3иду20. Это важно для крепежа и сантехники: без размера сотни болтов выглядели бы для подбора совпадений одинаково. Артикул в начале названия («D 46 Колесо») тегом не становится — его никто не ищет. - Товар без единого тега не пропадёт. Если после разбора тегов не нашлось (короткое название вроде «Засов гаражный», где оба слова ушли в название намерения), тегом станет слово самого названия. Раньше такой товар не импортировался вовсе с причиной «укажите хотя бы один тег»; теперь он публикуется, просто набор тегов у него беднее.
- Территория — только из профиля организации. YML город не несёт, поэтому сервис берёт город из профиля организации (раздел «Профиль» личного кабинета). Проще всего задать город один раз там — он подхватится для всех товаров. Если города в профиле нет, товар НЕ импортируется — в отчёте будет причина «не указан город»: «Вся страна» совпадает только со «Всей страной» (см. «Как это работает» → «Территория»), то есть импорт без города плодил бы намерения, не способные ни с кем совпасть.
- price — цена в рублях. Фактически ОБЯЗАТЕЛЬНА: импорт всегда создаёт намерения «Могу продать», а для этого типа по умолчанию нужна цена больше нуля (шаг «Стоимость» мастера обязателен, пока администратор сервиса явно не отключит его для этого типа в настройках конструктора мастера — по умолчанию включён). Импорт никогда не проставляет «цена договорная» автоматически — если
priceне указан или ≤ 0, сервис не создаст намерение и вернёт по товару ошибку «price: укажите стоимость или «договорная»» вerrorsотчёта (раздел 5).
Все загруженные товары становятся намерениями «Могу продать» — другие типы (аренда, услуги, вакансии) через этот канал сейчас недоступны. Количество — не отдельное поле оффера, а тоже характеристика в <param>: сервис ищет среди них ЛЮБУЮ с именем, содержащим «количество в упаковке» (регистр не важен), и её числовое значение становится количеством намерения — единица берётся из суффикса имени через запятую (например, «Количество в упаковке, шт» → 200 шт.; без суффикса единица останется не указана). Нет такой характеристики или её значение не число — количество принимается за 1 шт. Состояние товара при импорте всегда «новое» — это не настраивается.
4. Синхронизация и обновления
- id связывает запись файла с намерением в сервисе: повторная загрузка того же
idобновляет намерение на месте (без версий, как и везде в сервисе), а не создаёт дубль. - Товар, пропавший из очередного файла, сам по себе никак не меняется — намерение остаётся опубликованным. Чтобы такие товары закрывались автоматически, добавьте
remove_missing=1к запросу (push) или включите тумблер «Закрывать пропавшие из фида» (импорт по ссылке). - Файл без товаров мы не применяем. Если файл прочитался, но товаров в нём нет, загрузка отменяется целиком с ошибкой «фид не содержит товаров» — опубликованные намерения остаются на месте. Это защита от сбоя вашего сервера: отдай он пустой ответ или страницу ошибки, включённый флажок «Закрывать пропавшие из фида» снял бы с показа весь ваш каталог, а вернуть намерения в показ пришлось бы вручную по одному.
- Фотографии и видео, добавленные вручную, импорт больше не стирает (с 09.08.2026). Файл фида их не содержит и никогда не содержал, поэтому единственный способ показать фото у загруженного товара — добавить его самим. Раньше любая последующая загрузка с изменённой ценой или описанием снимала эти фото: сам файл в хранилище оставался, но переставал быть привязан к намерению. Теперь загрузка меняет только те поля, которые есть в файле.
- Загруженные намерения проходят те же проверки, что и созданные вручную: нормализация тегов, фильтр недопустимых слов, лимиты активных намерений — и публикуются сразу.
5. Ответ и коды
Отчёт о прогоне — один и тот же формат у прямого вызова и у «Проверить сейчас» (раздел 6), но они по-разному сообщают об отказе — это две разные вещи, их легко перепутать:
- Прямой вызов (
POST /v1/org/intentions/import) — обычный REST: успех —200с отчётом ниже, отказ — HTTP-код с текстом ошибки (коды — дальше в этом разделе). - «Проверить сейчас» (
POST /v1/org/feed/sync, раздел 6) отвечает200практически всегда, даже если сама синхронизация не удалась — из специально обрабатываемых исходов ответа всего один не 200, он описан в разделе 6. Смотрите полеokиfeed.last_messageв теле ответа, а не HTTP-код.
Успешный прогон (прямой вызов) отвечает 200:
{
"shop": "Мой магазин",
"dry_run": false,
"counters": {
"total": 2, "created": 1, "updated": 1,
"unchanged": 0, "skipped": 0, "closed": 0, "errors": 0
},
"skipped": [],
"errors": [],
"metrics": {
"total": 2, "converted": 2, "skipped": 0,
"category_hit_rate": 1, "avg_tags": 2.5,
"drop_reasons": { "вес 15 < порога 25": 1 }
}
}
skipped[] — товары с пустой или незнакомой категорией (раздел 3), у каждой записи id товара и причина. errors[] — остальные отказы по товару: нет id, нет цены, ни одна характеристика не дала тега, лимит намерений, недопустимое слово — тоже с id и текстом причины. metrics — сводка по ВСЕМУ фиду, не по отдельному товару: total/converted/skipped — всего офферов / распознано по категории / пропущено, category_hit_rate — доля распознанных категорий (converted/total), avg_tags — среднее число тегов на распознанный товар, drop_reasons — сколько раз и по какой причине отброшена ХАРАКТЕРИСТИКА при формировании тегов (раздел 3); привязки «какой товар» здесь нет — это агрегат для калибровки фида в целом, не диагностика конкретной позиции.
Коды прямого вызова:
401— токен не передан, отозван или неверен.403— аккаунт организации заблокирован либо тарифный план не оплачен (доступ по API приостановлен).409— либо пара «Хочу купить» / «Могу продать» временно отключена администрацией сервиса, либо импорт вашего фида уже выполняется (вы отправили два файла разом, или в этот момент сработал импорт по ссылке из раздела 6). Точная причина — в тексте ошибки. В обоих случаях повторите позже: прогоны по одной организации идут строго по очереди, иначе они мешали бы друг другу — в частности, прогон со старым файлом снял бы с публикации товары, которые только что добавил прогон со свежим.413— файл больше действующего предела размера.422— файл не похож на YML Яндекс.Маркета (нет XML-корняyml_catalog, битый синтаксис, вообще не XML) — не путать сskipped/errorsотдельного товара внутри валидного файла.
6. Импорт по ссылке (pull)
Альтернатива прямому вызову — без токена и без запросов с вашей стороны. В блоке «Импорт по ссылке» личного кабинета укажите URL файла (тот же формат, что в разделе 3) — сервис сам периодически его скачивает и синхронизирует намерения тем же конвейером.
- Тумблер «Обновлять автоматически» включает периодическую выкачку; частоту (по умолчанию раз в 6 часов) настраивает поддержка сервиса.
- Тумблер «Закрывать пропавшие из фида» — то же самое, что
remove_missingу прямого вызова, но для pull-режима. - Кнопка «Проверить сейчас» запускает синхронизацию немедленно. Ответ практически всегда
200вида{ "ok": true|false, "feed": {...} }— сам HTTP-код не сигнализирует об отказе (в отличие от прямого вызова, раздел 5): любая ошибка синхронизации (не выкачался файл, файл не YML, отключена пара, не оплачен тариф и т. д.) — этоok: falseи человекочитаемый текст вfeed.last_message, а не отдельный HTTP-код. Исключений два:422, если ссылка на фид ещё не сохранена (сначала заполните и сохраните поле URL), и409, если импорт вашего фида в этот момент уже идёт (например, вы одновременно отправили файл напрямую) — тогда статус фида не меняется, просто дождитесь завершения. При удачеfeed.last_countersсодержит те же счётчики, чтоcountersпрямого вызова (раздел 5). - Ссылка должна быть публичной (обычный http/https, без входа) — сервис не проходит авторизацию на стороне вашего файлового хранилища.
7. Ограничения
- Размер файла — по умолчанию не больше 60 МБ, товаров в файле — не больше 60 000. Оба предела настраиваются оператором сервиса, поэтому текущие значения уточняйте в поддержке; при превышении приходит
413с указанием предела. - Территория — по справочнику ФИАС/DaData (регион, город, адрес до дома) или «вся страна».
- Крупный файл обрабатывается не мгновенно: само чтение файла быстрое (около полусекунды на 20 тысяч товаров), но дальше каждый товар проходит проверки и запись, и это занимает время — до полутора-двух минут на большой каталог. Это ожидаемо, не сбой.
- При слишком частых запросах сервис может ответить
429— это защита от перегрузки; сделайте паузу и повторите позже. Для регулярной синхронизации предпочтительнее импорт по ссылке (раздел 6) с разумным интервалом, а не непрерывные повторы вручную. - Матчинг, статусы и остальные лимиты — целиком на стороне сервиса; файл задаёт только исходные данные товара.
Оба способа уже доступны без ожидания — не нужно ничего согласовывать заранее: выпустите ключ или укажите ссылку на файл в личном кабинете организации, разделы «Подключение по API» и «Импорт по ссылке». Вопросы по формату или подключению — в чат «Идеи и предложения» (первый в списке ваших чатов) или на почту поддержки.
Если вы перестали поставлять фид
Убрали ссылку или выключили фид — сервис считает это остановкой поставки. Ваши намерения из этого фида останутся в выдаче ещё неделю, а потом будут сняты с публикации. Точную дату вы увидите в личном кабинете, в блоке «Импорт по ссылке», и получите уведомление в момент остановки.
Почему так: после остановки цены и наличие в ваших намерениях никто не обновляет, и люди находят карточки, которым нельзя верить.
Три вещи, которые стоит знать:
- это обратимо. Вернули ссылку и включили фид до истечения срока — снятие отменяется, ничего делать не нужно;
- намерения не удаляются, а снимаются. История и диалоги сохраняются; чтобы вернуть их в выдачу позже, укажите ссылку и запустите импорт;
- намерения, созданные вами вручную, не трогаются. Правило касается только того, что пришло из фида.
Если вы просто меняете адрес фида — недели с запасом хватает, и делать ничего не надо.