+7 938 150-78-77

Обмен с МойСклад по API: как устроена синхронизация товаров, остатков и заказов

Не обзор возможностей, а механика: какие сущности читать, почему остатки лежат отдельно от карточки, откуда берутся копейки в ценах и что делать с лимитом запросов.

М Матвей Калашников Основатель студии 09 ноября 2026 обновлено 10 ноября 2026 12 минут чтения
Обмен с МойСклад по API: как устроена синхронизация товаров, остатков и заказов

Когда учёт ведётся в облаке, подключение сайта выглядит проще, чем с 1С: не нужен сервер обмена, не нужны папки с правами на запись, не нужен протокол с этапами. Но простота обманчива ровно в одном месте — в структуре данных. Разберём, как на самом деле устроена синхронизация, если делать обмен сайта с МойСклад не через готовый модуль, а понимая, что происходит.

Чем это отличается от CommerceML

Главное отличие — направление инициативы. В обмене с 1С инициатором выступает учётная система: она сама стучится на сайт, авторизуется, загружает файлы и ждёт ответа. С облачным учётом наоборот: сайт сам ходит за данными по HTTP, когда ему удобно, и получает JSON вместо XML.

Базовый адрес выглядит так: https://api.moysklad.ru/api/remap/1.2, дальше путь до нужной сущности. Никаких файлов выгрузки не существует вообще — есть набор ресурсов, каждый из которых отдаёт страницу данных. Это удобнее в отладке: любой запрос можно повторить руками и увидеть ровно то, что видит сайт.

Плата за удобство — нет готового механизма на стороне платформы. Если на 1С-Битрикс обмен по CommerceML работает из коробки, то синхронизацию с облачным учётом почти всегда пишут под проект. Что выбрать в принципе, мы разбирали в отдельной статье, здесь речь о том, как это работает после того, как выбор сделан.

Токен и права: с чего начинается подключение

Авторизация бывает двух видов: логин с паролем в заголовке Basic или токен в заголовке Authorization: Bearer. Для сайта правильный вариант только второй, и причины не в красоте.

  • Токен выдаётся отдельному служебному пользователю — его права видно и можно сузить до нужных разделов
  • Смена пароля сотрудника не роняет обмен, потому что обмен к этому сотруднику не привязан
  • Токен отзывается в один клик, если его засветили в репозитории или в переписке
  • В журнале действий видно, что изменил именно сайт, а не человек — при разборе спорных ситуаций это решает
  • Служебному пользователю не нужны права на удаление: сайт никогда ничего не удаляет в учёте

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

Товар, модификация, комплект, услуга — четыре разные сущности

Здесь спотыкаются чаще всего, потому что в каталоге сайта это обычно одна таблица. В учёте — четыре независимых справочника с разными полями.

  • Товар — карточка номенклатуры без вариантов. Путь <code>entity/product</code>
  • Модификация — конкретный вариант товара: размер, цвет, фасовка. Путь <code>entity/variant</code>, у каждой есть ссылка на родительский товар и набор характеристик
  • Комплект — набор, который продаётся как единица, но собирается из других позиций. Путь <code>entity/bundle</code>, остаток у него считается по компонентам
  • Услуга — позиция без остатка вообще. Путь <code>entity/service</code>

Если тянуть только товары, каталог приедет без вариантов: у магазина одежды вместо тридцати позиций будет десять, и ни у одной не будет размеров. Если тянуть только модификации — потеряются товары без вариантов. Поэтому в каталоге сайта нужно заранее решить, что считается карточкой, а что торговым предложением, и держать это соответствие.

Товар и модификация в учёте — объекты с разными идентификаторами. Карточка на сайте может соответствовать товару, а цена и остаток при этом живут на модификациях. Пока это не разведено в структуре базы сайта, обмен будет приносить непредсказуемые цифры.

Ассортимент: одна ручка вместо четырёх

Чтобы не собирать каталог из четырёх запросов, есть сводный ресурс entity/assortment. Он отдаёт всё, что можно продать, одним списком, и у каждой записи в поле meta.type указано, что это за сущность: product, variant, bundle или service.

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

Остатки лежат не в карточке товара

Вторая классическая ошибка. В карточке товара остатка нет — он считается по документам и отдаётся отчётами: report/stock/all для сводных цифр и report/stock/bystore с разбивкой по складам.

В отчёте не одно число, а несколько, и путать их нельзя:

  • stock — физический остаток, то, что лежит на складе
  • reserve — зарезервировано под заказы покупателей
  • inTransit — ожидается от поставщика
  • quantity — доступное к продаже: физический остаток минус резерв плюс ожидание

На витрину обычно уходит quantity, иногда — stock минус резерв без учёта ожидаемого. Выбор не технический: если показывать ожидаемое, сайт будет продавать то, что ещё не приехало. Решение принимает тот, кто отвечает за товародвижение, и фиксируется оно до начала работ, а не после первой жалобы. Почему цифры в учёте и на сайте расходятся даже при исправном обмене, мы подробно разбирали здесь — для облачного учёта список причин почти тот же.

Полезная деталь: у отчёта по остаткам есть фильтр режима, который позволяет получать только положительные остатки. На каталоге в десятки тысяч позиций это сокращает ответ в разы и экономит те самые лимиты запросов, про которые ниже.

Цены: массив, типы и копейки

Цена в учёте — не одно число. В карточке лежит массив цен продажи, у каждой свой тип: розничная, оптовая, дилерская, акционная. Отдельно хранятся закупочная цена и минимальная цена.

  • Типы цен настраиваются в учёте и имеют свои идентификаторы — сайт должен знать, какой из них его, и брать цену по типу, а не по порядковому номеру в массиве
  • Все суммы приходят в копейках: 1490 рублей в ответе выглядит как 149000. Делить на сто нужно один раз и в одном месте кода
  • Закупочную цену на сайт не отдают никогда — её даже не стоит тянуть в запросе, если служебному пользователю можно эти права не давать
  • Если на сайте есть личный кабинет с дилерскими ценами, тип цены выбирается по контрагенту, и это отдельная логика, а не поле в карточке
  • Скидки, рассчитанные в учёте, и акции, настроенные на сайте, складываются непредсказуемо — источник правды по скидкам должен быть один

Страницы, лимиты и код 429

Ответ всегда постраничный. Количество записей задаётся параметром limit, смещение — offset, в метаданных ответа приходит общее количество. Максимальный размер страницы ограничен, и при использовании expand для подтягивания связанных объектов ограничение становится жёстче — это нормально, но код обхода страниц должен это учитывать, а не предполагать, что всё приедет за один раз.

Важнее другое: на частоту обращений есть лимит. Он считается в запросах за короткий интервал и ограничивает число одновременных соединений. При превышении приходит код 429 и в заголовках — сколько ждать до следующей попытки. Конкретные цифры лежат в документации и со временем меняются, поэтому полагаться надо не на них, а на правильное поведение клиента.

  • Обработка 429 с повторной попыткой и нарастающей паузой — обязательна, без неё обмен будет падать на больших каталогах случайным образом
  • Параллельных запросов немного и больше не станет: обходить каталог лучше последовательно, но большими страницами
  • Обмен не должен запускаться из веб-запроса пользователя: только из очереди или по расписанию, иначе страница отвалится по таймауту на середине
  • Два одновременных обмена одного и того же — худший сценарий. Нужна блокировка, которая не даёт запуститься второму экземпляру
  • Любой ответ с кодом 5xx на стороне сервиса — это повод повторить попытку позже, а не повод считать товар отсутствующим

Последний пункт звучит очевидно, но именно на нём теряют каталоги: обмен не смог получить данные, получил пустой ответ, добросовестно записал нулевые остатки, и магазин на несколько часов остался без товаров в наличии. Правильная логика — пустой или ошибочный ответ не меняет ничего.

Что тянуть целиком, а что по изменениям

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

  • Остатки и цены — часто, по изменениям, с запасом по времени на случай расхождения часов
  • Описания, характеристики, картинки — редко, полным проходом, ночью
  • Полная сверка раз в сутки всё равно нужна: она ловит то, что по какой-то причине не приехало инкрементально
  • Время последнего успешного обмена нужно хранить и показывать в админке — это первый вопрос при любом разборе
  • Товары, пропавшие из учёта или помеченные архивными, скрываются с витрины, но страница остаётся: удалять нельзя, она уже в индексе

Заказы в обратную сторону

Заказ покупателя создаётся как документ entity/customerorder, и у него обязательные связи: организация, от лица которой продаём, контрагент-покупатель и склад, с которого отгружаем. Позиции заказа ссылаются на конкретную сущность ассортимента — товар, модификацию или комплект — и содержат количество и цену в копейках.

  • Покупателя надо во что-то превратить: искать существующего контрагента по телефону или почте либо создавать нового. Решение принимает тот, кто ведёт учёт, иначе справочник контрагентов за год превращается в свалку дублей
  • У документа можно задать внешний идентификатор — тот же номер, что на сайте. Без него сверка заказов превращается в сопоставление по дате и сумме
  • Повторная отправка одного и того же заказа при сбое не должна создавать второй документ: проверка по внешнему идентификатору перед созданием обязательна
  • Доставка, сборка, скидка — это или отдельные позиции в документе, или поля документа. Как именно, зависит от того, как потом считают выручку
  • Статусы заказа на сайте и статусы документа в учёте — разные справочники. Нужна таблица соответствия, и в ней должно быть указано, чей статус главнее при расхождении

Отдельно про оплату: факт оплаты на сайте и приходный документ в учёте — не одно и то же. Если нужна связка с онлайн-кассой и эквайрингом, это проектируется сразу, потому что дописывать её к работающему обмену дороже.

Вебхуки: когда помогают, а когда мешают

Вместо того чтобы спрашивать учёт об изменениях, можно попросить его сообщать самому. Подписка настраивается на событие создания, изменения или удаления сущности, и на указанный адрес приходит уведомление.

  • Обработчик должен отвечать мгновенно и ставить задачу в очередь, а не обрабатывать на месте: медленный ответ приводит к повторным попыткам
  • Уведомление может прийти дважды — обработчик обязан быть идемпотентным
  • Уведомление может не прийти вовсе: сеть, перезапуск, ошибка. Поэтому вебхуки дополняют регулярную сверку, а не заменяют её
  • Массовая правка цен в учёте породит лавину уведомлений — очередь должна их склеивать, а не обрабатывать каждое
  • Адрес обработчика должен быть доступен снаружи по HTTPS, что не всегда так на тестовых контурах

Практический вывод: вебхуки хороши для заказов и срочных изменений остатков по ходовым позициям. Весь каталог на них строить не стоит — получится система, которая работает, пока не пропустит одно уведомление, и после этого расходится молча.

Что ломается на практике

  • Сопоставление потеряно: товары на сайте привязаны по артикулу, артикул в учёте изменили — каталог задублировался. Привязка должна быть по идентификатору сущности, он не меняется никогда
  • Остаток берётся с товара, а варианты не учтены: витрина показывает сумму по всем размерам
  • Цена не делится на сто или делится дважды — ловится на первой же карточке, если проверять глазами, а не только логами
  • Обмен запускается по расписанию, но предыдущий ещё идёт: два процесса пишут в одну таблицу
  • Кеш каталога не сбрасывается после обмена: в базе данные свежие, на витрине вчерашние
  • Картинки тянутся при каждом обмене заново — обмен работает часами вместо минут. Скачивать надо только изменившиеся
  • Нет уведомления о сбое: обмен не работает три дня, узнают от покупателя
  • Товар скрыт в учёте как архивный, а на сайте остался в продаже, потому что обработан только признак остатка

Порядок внедрения, который себя оправдывает

  • Зафиксировать источник правды по каждому полю: цена, остаток, название, описание, картинки — что приезжает из учёта, а что ведётся на сайте
  • Решить, что на сайте является карточкой, а что торговым предложением, и как это соответствует товарам и модификациям
  • Выбрать показатель остатка и склады, с которых продаёт сайт
  • Выбрать тип цены и описать, что происходит со скидками
  • Сделать односторонний обмен каталога и проверить его на десятке контрольных позиций руками
  • Добавить обмен заказами с проверкой на повторную отправку
  • Настроить расписание, блокировку одновременного запуска и уведомления о сбоях
  • Записать в журнал время каждого обмена и вывести его в админку

Первые четыре пункта — не разработка, а решения владельца бизнеса. Их отсутствие и есть основная причина, по которой интеграции переделывают: код работает ровно так, как его попросили, а попросили исходя из догадок.

Мы делаем синхронизацию с МойСклад под конкретный каталог, с расписанием, журналом и уведомлениями о сбоях, а не как разовый скрипт. Если каталог только проектируется, интеграцию логичнее заложить в проект сразу — смотрите разработку интернет-магазина. Если в учёте уже лежат остатки, которые должны одновременно уезжать и на сайт, и на площадки, это соседняя задача — выгрузка на маркетплейсы. Обмен, который уже работает и требует присмотра, ведётся в рамках поддержки.

Как проверить, что повторный обмен не создаёт дубли

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

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

Точные форматы и ограничения проверяйте по документации JSON API МойСклад для используемой версии. В Funny Jobs применена двусторонняя связь с магазином. Для оценки вашего обмена нужны CMS, схема данных и пример товара или заказа.

М Матвей Калашников · основатель Sapphireweb 6 лет делает сайты и интеграции на 1С-Битрикс и WordPress. Пишет о том, что чинит своими руками. Статья опубликована 09 ноября 2026, последнее обновление — 10 ноября 2026. Пересматриваем материалы раз в квартал и правим то, что устарело.
Из наших проектов

Как решали похожие задачи

В кейсах — исходная задача, выполненные работы и результат конкретного проекта. Состав решения для вашего сайта определим отдельно.

Вопросы

Отвечаем
заранее

Не нашли свой вопрос? Напишите в Telegram — ответим без долгих согласований.

Это REST API с запросами в реальном времени, а не выгрузка файлов по расписанию. Обмен получается точнее, но требует учёта лимитов и постраничного чтения.
В МойСклад остатки лежат в отдельном отчёте, а не в карточке. Их запрашивают своим вызовом и сопоставляют с ассортиментом по идентификатору.
Это превышение лимита запросов. Нужны пауза с повтором, постраничное чтение и синхронизация по изменениям, а не выгрузка всего ассортимента каждый раз.

Расскажите
о задаче

Пришлите ссылку на сайт и опишите задачу. Уточним исходные данные, предложим состав работ и подготовим оценку стоимости и сроков. Пришлите сайт и задачу — подготовим оценку работ, стоимости и сроков.

Вопрос по статье: Обмен с МойСклад по API: как устроена синхронизация товаров, остатков и заказов