# Загрузка через REST API

Есть ситуации, когда использование товарного каталога YML невозможно:

  • Слишком много товаров в базе (больше 200 000).
  • Слишком большой файл XML-файл (больше 300 мегабайт).
  • Часто появляются новые товары (в небольших количествах) и часто товары заканчиваются на складе (тоже в небольших количествах), поэтому загружать сотни тысяч товаров ради изменения нескольких сотен расточительно по трафику и ресурсам.
  • Необходимо оперативно управлять наличием товаров (чаще, чем 1 раз в 3 часа).

Для этого разработан REST API.

Rate limit

Лимит запросов к API (Rate Limit) ограничен 40 запросами в минуту.

Лимит на вес запроса к API (Weight Limit) ограничен 35 мегабайтами на запрос.

Для On-premise лимитов нет.

# Порядок импорта

Обратите внимание – загружать товары можно только после выполнения подготовительных загрузок категорий и городов.

Последовательность шагов:

  1. Загрузка информации о категориях.
  2. Загрузка информации о городах/локациях.
  3. Первичная загрузка всех товаров.
  4. Итеративное обновление товарного каталога по мере необходимости.

# Загрузка категорий

XML

Не используйте этот метод, если используете способ загрузки товаров через YML/XML.

Прежде, чем загружать товары, необходимо загрузить в базу данных актуальный перечень категорий. Загружайте только те категории, которые используются в товарах (и промежуточные элементы дерева категорий). Нет смысла загружать конечные элементы дерева категорий (листья), в которых нет товаров.

# Описание метода

POST https://api.rees46.ru/import/categories

Необходимо указать заголовок:

Content-Type: application/json

Данные отправляются в виде JSON-строки, которая является телом запроса.

# Жизненный цикл запроса

  1. Сервер API REES46 принимает запрос.
  2. Выполняет первичную проверку доступа (shop_id, shop_secret). Если shop_id и shop_secret не соответствуют существующим, сервер возвращает ошибку 400 Bad request.
  3. Отправляет задачу в фоновую обработку.
  4. Возвращает код 204 No body.
  5. В случае успешной обработки фоновой задачи ничего не происходит. Задача обрабатывается быстро, поэтому спустя минуту уже можно загружать информацию о товарах.
  6. В случае ошибочной обработки фоновой задачи приходит уведомление на адрес электронной почты сотрудников, подписанных на технические уведомления.

# Структура данных

{
    "shop_id":      "...",
    "shop_secret":  "...",
    "categories":   [{category}, {category}, ...]
}

Структура информации о категории:

{
    "id":     "...", // String. Required
    "name":   "...", // String. Required
    "parent": "...", // String or null. Optional
    "url":    "...", // String. Optional
    "alias":  "..."  // String. Optional
}

XML

Если не указывать url, в стандартном шаблоне быстрого поиска категория выводиться не будет. Базовая механика предусматривает переход на страницу категории при клике. При этом в ответе API категории будут возвращаться, что позволит вывести их в кастомном или самописном шаблоне.

Вложенность подкатегорий не ограничена.

# Пример тела запроса

{
    // Shop key, берется в личном кабинете на экране настроек магазина
    "shop_id":      "eehj3eu84299kg5ghw5a6743r8",  
     
    // Secret key, берется в личном кабинете на экране настроек магазина
    "shop_secret":  "pmd5362597thrgq8k256ep01t0",  
     
    // Список категорий
    "categories":   [
        {
            "id":           "1",
            "name":         "Главная категория",
            "parent":       null,
            "url":          "https://mysite.com/catalog"
        },
        {
            "id":           "2",
            "name":         "Одежда",
            "parent":       "1",
            "url":          "https://mysite.com/catalog/apparel"
        },
        {
            "id":           "3",
            "name":         "Велосипеды",
            "parent":       "1",
            "url":          "https://mysite.com/catalog/bicycles"
        },
        {
            "id":           "100",
            "name":         "Детские",
            "parent":       "3",
            "url":          "https://mysite.com/catalog/bicycles/child",
            "alias":        "bicycles/child"
        }
    ]
}

# Загрузка локаций

XML

Не используйте этот метод, если используете способ загрузки товаров через YML/XML.

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

# Описание метода

POST https://api.rees46.ru/import/locations  

Необходимо указать заголовок:

Content-Type: application/json

Данные отправляются в виде JSON-строки, которая является телом запроса.

# Жизненный цикл запроса

  1. Сервер API REES46 принимает запрос.
  2. Выполняет первичную проверку доступа (shop_id, shop_secret). Если shop_id и shop_secret не соответствуют существующим, сервер возвращает ошибку 400 Bad request.
  3. Отправляет задачу в фоновую обработку.
  4. Возвращает код 204 No body.
  5. В случае успешной обработки фоновой задачи ничего не происходит. Задача обрабатывается быстро, поэтому спустя минуту уже можно загружать информацию о товарах.
  6. В случае ошибочной обработки фоновой задачи приходит уведомление на адрес электронной почты сотрудников, подписанных на технические уведомления.

# Структура данных

{
    "shop_id":      "...",
    "shop_secret":  "...",
    "locations":    [{location}, {location}, ...]
}

Структура информации о локации:

{
    "id":     "...", // String. Required
    "name":   "...", // String. Required
    "parent": "...", // String || Null. Required
    "type":   "...", // String. Optional
    "group":  "...", // String or Array of string. Optional
    "discount_percent ":  "..." // Optional,
    "search_level_up": "..." // Integer. Optional
}

Обратите внимание

Как считается discount_percent при отсутствии значения (или самого параметра):

  • На корневом уровне товара:

    • Если параметр указан — используется напрямую.
    • Если параметр не указан, но есть oldprice и price — значение вычисляется как целая часть от ((oldprice - price) / oldprice) * 100 (путём округления вниз до целого).
  • На уровне локаций:

    • Работает по тем же правилам, что и на корневом уровне.
    • Если в локации не указаны oldprice или price, для расчёта используются значения из корня товара.

Вложенность элементов не ограничена.

# Пример тела запроса

{
    // Shop key, берется в личном кабинете REES46 на экране настроек магазина
    "shop_id":      "eehj3eu84299kg5ghw5a6743r8",  
     
    // Secret key, берется в личном кабинете REES46 на экране настроек магазина
    "shop_secret":  "pmd5362597thrgq8k256ep01t0",  
     
    // Список локаций
    "locations":    [
        {
            "id":           "1",
            "name":         "Москва",
            "parent":       null,
            "type":         "city",
            "group":        "ДенежныеГорода",
            "discount_percent ":  5
        },
        {
            "id":           "145",
            "name":         "Пункт выдачи на Арбате",
            "parent":       "1",
            "type":         "store",
            "search_level_up": 1
        },
        {
            "id":           "2",
            "name":         "Санкт-Петербург",
            "parent":       null,
            "type":         "city",
            "group":        "ДенежныеГорода",
            "discount_percent ":  5
        },
        {
            "id":           "3",
            "name":         "Приморский край",
            "parent":       "1",
            "type":         "state",
            "search_level_up": 0
        },
        {
            "id":           "4",
            "name":         "Владивосток",
            "parent":       "3",
            "type":         "city",
            "group":        "ДальнийВосток",
            "discount_percent ":  5,
            "search_level_up": 1
        }
    ]
}

# Загрузка товаров

# Описание метода

PUT https://api.rees46.ru/import/products  

Необходимо указать заголовок:

Content-Type: application/json

Данные отправляются в виде JSON-строки, которая является телом запроса.

Есть следующие виды запросов:

Название операции Тип HTTP запроса Описание
Добавление/изменение PUT Добавить новые товары, не отключая существующие, либо обновить существующие.
Синхронизация PATCH Синхронизировать наличие товаров.
Удаление DELETE Удалить товары, перечисленные в запросе (отметить как "не в наличии").

# Жизненный цикл запроса

  1. Сервер API REES46 принимает запрос.
  2. Выполняет первичную проверку доступа (shop_id, shop_secret). Если shop_id и shop_secret не соответствуют существующим, сервер возвращает ошибку 400 Bad request.
  3. Отправляет задачу в фоновую обработку.
  4. Возвращает код 204 No body.
  5. В случае успешной обработки фоновой задачи ничего не происходит. Задача обрабатывается быстро, поэтому спустя минуту уже можно загружать информацию о товарах.
  6. В случае ошибочной обработки фоновой задачи приходит уведомление на адрес электронной почты сотрудников, подписанных на технические уведомления.

Обратите внимание

Все ключи регистрозависимы.

# Описание данных в запросе

{
    "shop_id":      "...",
    "shop_secret":  "...",
    "items":        [{item1}, {item2}, ...]
} 

Пример структуры объекта товара item:

{
  "id": "string", // required
  "available": true,
  "categories": ["string", 1], // required, array of strings or integers
  "picture": "https://example.com/image.jpg", // URL, required, max. 500 characters
  "name": "string", // required
  "price": 99.99, // required, >0
  "url": "https://example.com/product", // required
  "oldprice": 120.00,
  "params": [
    {
      "name": "weight", //required
      "value": [70.5], //Array.Required
      "unit": "kg", // Optional
      "priority": 1, // Optional
      "searchable": true
    },
    {
      "name": "color",
      "value": ["red", "blue"],
      "unit": null,
      "priority": 2,
      "searchable": false
    }
  ],
  "group_id": "group123",
  "type_prefix": "prefix",
  "brand": "Brand Name",
  "brand_picture": "https://example.com/brand.jpg",
  "vendor_code": "ABC123",
  "barcode": "1234567890123",
  "model": "Model Name",
  "description": "Product description",
  "merchant": ["First merchant's name","second merchant's name", "third merchant's name"],
  "tags": ["tag1", "tag2"],
  "stock_quantity": 10,
  "discount_percent": 15,
  "gift": "Gift Name",
  "installment": 12,
  "promocode": "DISCOUNT10",
  "price_with_promocode": 89,
  "deeplink_android": "app://product/123",
  "deeplink_ios": "app://product/123",
  "bonuses_accrual_allowed": true,
  "bonuses_spending_allowed": false,
  "discounts_allowed": true,
  "accessories": ["Accessory1", "Accessory2"],
  "customer_recommendations": ["Product1", "Product2"],
  "seasonality": [1, 2, 12], // array of numbers 1-12
  "rating": 3, // number 1-5
  "leftovers": "few",
  "is_new": true,
  "fashion": {
    "gender": "m",
    "type": "clothing",
    "sizes": ["S", "M", 42],
    "colors": [
      {
        "color": "red",
        "picture": "https://example.com/red.jpg"
      }
    ],
    "feature": "adult" // e.g., 'adult'
  },
  "child": {
    "type": "toy",
    "gender": "f",
    "age": {
      "min": 3,
      "max": 7
    }
  },
  "jewelry": {
    "gender": "f",
    "color": "gold",
    "metal": "gold",
    "gem": "diamond",
    "ring_sizes": [6, "7.5"],
    "bracelet_sizes": ["S", "M"],
    "chain_sizes": [40, 50]
  },
  "cosmetic": {
    "gender": "f",
    "hypoallergenic": true,
    "periodic": false,
    "skin": {
      "part": ["face", "hands"],
      "type": ["dry", "oily"],
      "condition": ["acne", "wrinkles"]
    },
    "hair": {
      "type": ["curly", "straight"],
      "condition": ["damaged", "dry"]
    },
    "nail": {
      "type": "gel",
      "polish_color": "red"
    },
    "perfume": {
      "aroma": "floral",
      "family": "woody"
    },
    "professional": true
  },
  "pharmacy": {
    "volumes": [
      {
        "value": 100,
        "price": 19.99
      }
    ]
  },
  "book": {
    "author": "Author Name",
    "publisher": "Publisher Name",
    "series": "Book Series",
    "editor": "Editor Name",
    "illustrator": "Illustrator Name",
    "year": 2022, // 1700 < year < current year
    "isbn": ["978-3-16-148410-0"]
  },
  "realty": {
    "action": "rent",
    "type": "apartment",
    "space": {
      "min": 50,
      "max": 120,
      "final": 100
    }
  },
  "auto": {
    "vds": "engine oil",
    "periodic": true,
    "compatibility": [
      {
        "brand": "Toyota",
        "model": "Camry"
      }
    ]
  },
  "fmcg": {
    "hypoallergenic": false,
    "periodic": true
  },
  "pets": {
    "periodic": true,
    "breed": "Labrador",
    "type": "dog",
    "age": "puppy",
    "size": "large"
  },
  "date": "2025-03-03T12:00:00Z", // valid date
  "price_margin": 20, // 0-100
  "ignored": false
}

Примите во внимание

При обработке url к ссылке добавляются параметры вида recommended_by, recommended_code и т.д. Они необходимы для работы трекинга событий и построения аналитики.

Если вам нужно передать в ссылке собственные параметры, их требуется закодировать. В противном случае некоторые символы будут некорректно обработаны, например, согласно спецификации RFC3986 знак + распознается как пробел. Если вам нужно, чтобы при переходе по ссылке в параметре передавался знак +, то его нужно экранировать в %2B.

Пример:

http://test.com?test=+-!ABC -> http://test.com?test=%2B-%21ABC

Нужно учитывать

  1. Если ключ параметра пришёл пустым, параметр игнорируется и не добавляется в список параметров товара
  2. При отсутствии параметра discount_percent и наличии oldprice, первый будет автоматически рассчитан по формуле: целая часть от ((oldprice - price) / oldprice) * 100 (получается путём округления вниз до целого)

# Особенности location

Объект "Цена и наличие в локации" показывает, что товар есть в наличии, в определенной локации и его цена отличается от базовой цены. Если цена в объекте не указана, то для указанной локации будет использоваться базовая цена. Если объект "Цена и наличие в локации" указан, то во всех других локациях, которые не перечислены для данного товара в свойстве locations, товар будет считаться не в наличии.

{
    "location":        "...", // String. Required
    "price":            ...,  // Float (positive). Optional.
    "oldprice":         ...,  // Float (positive). Optional.
    "merchant":        ["...", "..."], // Array of strings. Optional.
    "stock_quantity":   ...,  // Int (positive). Optional.
    "sizes":           ["...", "..."], // Array of string. Optional.
    "weight":           ...,  // Int (positive). Optional.
    "delivery_types":  {
      "store":      ..., // Int (positive). Available in store
      "delivery":   ..., // Int (positive). Available in delivery
      "warehouse":  ... // Int (positive). Available in warehouse
    }, // Object. Optional
}

# Особенности delivery_types

Объект "Типы доставки" показывает, что товар доступен для покупки в магазине или со склада.

{
    "delivery_type_1":  ..., // Int. Required
    "delivery_type_2":  ..., // Int. Required
    "...":              ...,
}

# Особенности params

Пример вложенной структуры информации о параметрах params:

{
    "name":        "...",  // String. Required
    "value":      ["...", "..."], // Array. Required
    "unit":        "...", // String. Optional
    "priority":     ..., // Int. Optional
    "searchable":   ..., // Boolean (true, false)
} 

# Нишевые параметры

Для обогащения пользовательских профилей и усиления эффекта персонализации рекомендуется добавлять нишевые параметры товаров в секцию offer.

# Пример структуры

Лимит

Рекомендуется отправлять не более 5000 товаров в одном запросе.

Ниже представлен пример с заполненными данными и пояснениями:

{
    // Shop key, берется в личном кабинете REES46 на экране настроек магазина
    "shop_id":      "eehj3eu84299kg5ghw5a6743r8",  
     
    // Secret key, берется в личном кабинете REES46 на экране настроек магазина
    "shop_secret":  "pmd5362597thrgq8k256ep01t0",  
     
    // Список товаров
    "items":            [
        // Товар 1
        {
  
            // Идентификатор товара в магазине
            "id":           "6335",
            
            // Идентификатор группы товаров в магазине. Если передаются варианты товаров, этот параметр позволяет объеденить их в один товар
            "group_id":             "633",
  
            // Название
            "name":                 "Велосипед",
  
            // Цена
            "price":                13000, 
            
            // Цена с учётом применения промокода
            "price_with_promocode": 12000, 
            
            // Старая цена
            "oldprice":             16000, 
  
            // Валюта цены
            "currency":             "RUB",
  
            // URL товара, без UTM-меток и прочих параметров отслеживания источников перехода
            "url":                  "https://myste.com/products/6335.html",
            
            // URL товара в приложении Android
            "deeplink_android":     "https://myste.com/products/6335.html",
            
            // URL товара в приложении Ios
            "deeplink_ios":         "https://myste.com/products/6335.html",
  
            // URL фотографии товара
            "picture":              "https://mysite.com/pictures/6335.jpg",
  
            // Товар в наличии
            "available":            true,
  
            // Массив идентификаторов категорий, в которых лежит товар (не хлебные крошки, только конечные категории)
            "categories":           ["17", "3"],
  
            // Штрих-код товара
            "barcode":              "17333838374318",
  
            // Маржинальность товара 10%
            "price_margin":         10,
  
            // Наличие товаров в определенных городах: доступен только в Москве и СПб
            "locations": [
                // Есть в наличии в Москве и его цена равна базовой
                {
                    "location": "msk",
                    "delivery_types": {
                        "shop": 10, // В магазине 10 шт.
                        "stock": 50 // На складе 50 шт.
                    }
                },
                // Есть в наличии в СПб и его цена отличается
                {
                    "location": "spb",
                    "price":    12500
                }
            ],
  
            // Производитель
            "brand":                "Marine",
  
            // Характеризующие теги
            "tags":                 ["alluninium", "sport"],
  
            // Детский велосипед
            "is_child":             true,
 
            // Параметры товара
            "params": [
                {
                    "name": "интерфейс",
                    "value": ["bluetooth", "wi-fi"]
                },
                {
                    "name": "энергопотребление",
                    "value": [23],
                    "unit": "вт"
                },
                {
                    "name": "Сим карта",
                    "value": ["E-sim"],
                    "priority": 20
                },
		        {
			        "name": "Цвет лейбла",
			        "value": "Синий",
			        "searchable": false // Технический параметр исключается из блока filters
		        }
            ],
            
            // Мерчант
            "merchant":             "abc123xyz789",
            
            // Дата добавления|обновления
            "creation_date":        "2022-01-01",
            
            // Наличие подарка
            "gift":                 true,
            
            // Информация о рассрочке
            "installment":          36,
            
            // Информация о скидке
            "discount_percent":     50,
            
            // Информация о промокоде
            "promocode":            "Sale50",
          
            // Начисление бонусов за товар разрешено
            "bonuses_accrual_allowed": true,
          
            // Списание бонусов за товар разрешено
            "bonuses_spending_allowed": true,
          
            // Применение скидки на товар разрешено
            "discounts_allowed": true
          
        },
  
        // Товар 2
        {
            "id":           "133",
            "name":         "Куртка красная",
            "price":        123000,
            "oldprice":     132000, 
            "currency":     "RUB",
            "url":          "https://myste.com/products/133.html",
            "picture":      "https://mysite.com/pictures/133.jpg",
            "available":    true,
            "categories":   ["33"],
  
            // Доступен только в Москве
            "locations": [
                { "location": "msk" }
            ],
            "brand":        "Racoon",
            "tags":         ["winter", "sport"],
  
            // Это одежда
            "is_fashion":   true,
  
            "fashion": {
                // Мужская
                "gender":   "m",
  
                // В размерах 48, 50, 52 российской размерной сетки
                "sizes":    ["48", "50", "52"],
  
                // Тип: куртка
                "type":     "jacket"
            },
            // Дата добавления|обновления
            "date": "2022-01-01"  
        }
    ]
}

# Особенности отправки DELETE запроса

При отправке запроса типа DELETE (удаление товаров из платформы) достаточно перечислить идентификаторы товаров, которые необходимо удалить. Пример:

{
    "shop_id":      "eehj3eu84299kg5ghw5a6743r8",  
    "shop_secret":  "pmd5362597thrgq8k256ep01t0",  
    "items":        ["635", "3373", "75778"]
}

# Особенности отправки PATCH запроса

При отправке запроса типа PATCH (синхронизация наличия товаров в базе данных платформы) достаточно перечислить идентификаторы товаров, которые необходимо пометить как "в наличии". Товары из текущей базы данных платформы, идентификаторы которых не включены в PATCH запрос, будут помечены как "не в наличии".

Пример:

{
    "shop_id":      "eehj3eu84299kg5ghw5a6743r8",  
    "shop_secret":  "pmd5362597thrgq8k256ep01t0",  
    "items":        ["635", "3373", "75778"]
}

# Webhook запросы

Для автоматизации процесса импорта данных о товарах можно использовать функционал webhook запросов.

# Импорт категорий с webhook параметром

{
	"shop_id":      "...",
	"shop_secret":  "...",
	"categories":   ["category_id_1", "category_id_2", "..."],
	"webhook":      "https://site.com/webhook"
}

# Импорт локаций с webhook параметром

{
	"shop_id":      "...",
	"shop_secret":  "...",
	"locations":    ["location_id_1", "location_id_2", "..."],
	"webhook":      "https://site.com/webhook"
}

# Импорт товаров с webhook параметром

{
    "shop_id":      "...",  
    "shop_secret":  "...",  
    "items":        ["item_id_1", "item_id_2", "..."],
    "webhook":      "https://site.com/webhook"
}

После того, как запрос будет обработан, на указанный вебхук будет отправлен POST запрос c типом Content-Type: application/json

Примеры:

{
	"status": "success"
}
{
	"status": "error", 
	"message": "MESSAGE"
}