Яндекс.Маркет API: карточки, поиск и фильтры

Ищите товары по словам, уточняйте выдачу фильтрами и получайте карточку по ссылке. Ниже — метод карточки; параметры поиска и фильтров описаны в отдельных разделах. Для вызова нужен API-токен BHAPI с доступом к парсеру Яндекс.Маркет.

POST/yamarket/api/v1/item/detail-by-url

Запрос

Параметры JSON-запроса

Тело POST-запроса
ПараметрТипОбязательныйОписание
urlstringдаПолный URL карточки Яндекс.Маркета, 1–8192 символов. Единственное поле JSON; тело не более 16 КиБ.

Заголовки

Заголовки POST-запроса
ЗаголовокОбязательныйОписание
X-API-TokenдаAPI-токен BHAPI с доступом к Яндекс.Маркет. Передавайте в заголовке.
Content-Typeдаapplication/json; charset допустим.

Принимается HTTPS-ссылка https://market.yandex.ru/card/<slug>/<osku_id>. Передавайте только url. Подробные ограничения описаны в правилах URL.

Пример запроса

cURL
curl --max-time 180 --fail-with-body -i -X POST "https://bhapi.ru/yamarket/api/v1/item/detail-by-url" \
  -H "Content-Type: application/json" \
  -H "X-API-Token: ВАШ_API_TOKEN" \
  --data '{"url":"https://market.yandex.ru/card/example-product/000100000001"}' 

Проверяйте HTTP status и Retry-After в заголовках. Не считайте null цены нулём.

Ниже показан ответ для этого запроса. Все значения примера синтетические.

Успешный ответ 200 OK

HTTP 200: {"status":"ok","data":ProductData}. Все значения ниже синтетические; служебный envelope источника наружу не передаётся.

JSON
{
  "status": "ok",
  "data": {
    "item_id": "000100000001",
    "product_url": "https://market.yandex.ru/card/example-product/000100000001",
    "title": "Синтетический чайник",
    "brand": "Пример",
    "desc": "Учебное описание товара",
    "currency": "RUB",
    "price_info": {
      "sale_price": "1200.50",
      "origin_price": "1500"
    },
    "main_imgs": [
      "https://cdn.example.org/kettle.jpg"
    ],
    "main_videos": [
      "https://cdn.example.org/kettle.mp4"
    ],
    "category_path": [
      {
        "id": "category-1",
        "name": "Чайники",
        "url": "https://market.yandex.ru/catalog--kettles/1"
      }
    ],
    "product_props": [
      {
        "group": "Общие",
        "name": "Объём",
        "value": "1.5 л"
      }
    ],
    "shop_info": {
      "shop_name": "Учебный магазин",
      "shop_url": "https://market.yandex.ru/business--example/1"
    },
    "delivery_info": {
      "options": [
        {
          "type": "delivery",
          "day_from": 1,
          "day_to": 3,
          "date": null,
          "price": "0",
          "currency": "RUB",
          "partner_type": "market"
        }
      ],
      "context": "anonymous_session"
    },
    "review_info": {
      "rating_star": 4.5,
      "rating_count": 12,
      "review_count": 8
    },
    "sku_props": [
      {
        "name": "Цвет",
        "values": [
          "Синий",
          "Белый"
        ]
      }
    ],
    "skus": [
      {
        "item_id": "000100000002",
        "offer_id": "offer-variant",
        "product_url": "https://market.yandex.ru/product--example-blue/100000002",
        "properties": {
          "Цвет": "Синий"
        },
        "price": "1250",
        "currency": "RUB",
        "available": true
      }
    ],
    "market": {
      "schema_version": "1.0",
      "ids": {
        "osku_id": "000100000001",
        "catalog_sku_id": "catalog-1",
        "product_id": "product-1",
        "offer_id": "offer-main",
        "business_id": "business-1",
        "shop_id": "shop-1",
        "supplier_id": "supplier-1",
        "vendor_id": "vendor-1",
        "category_id": "category-1",
        "navnode_id": "navnode-1"
      },
      "availability": {
        "in_stock": true,
        "available_count": 7
      },
      "price_context": {
        "anonymous": true,
        "is_personal": true,
        "discount_percent": 19.97
      },
      "seller": {
        "business_id": "business-1",
        "shop_id": "shop-1",
        "rating": 4.8,
        "rating_count": 40,
        "reviews_count": 30,
        "orders_count": 100,
        "subscribers_count": 20,
        "age_text": "2 года",
        "logo_url": "https://cdn.example.org/shop-logo.png",
        "legal_info": [
          {
            "legal_form": "ООО",
            "name": "Учебный продавец",
            "supplier_name": "Учебный поставщик",
            "inn": "synthetic-inn",
            "ogrn": "synthetic-ogrn"
          }
        ]
      },
      "manufacturer": {
        "brand_name": "Пример",
        "legal_name": "Учебный производитель",
        "country": "Россия"
      },
      "reviews": [
        {
          "id": "review-1",
          "rating": 4,
          "date_text": "12 октября 2026",
          "pro": "Учебное достоинство",
          "contra": null,
          "comment": "Синтетический отзыв",
          "votes_agree": 2,
          "votes_reject": 0,
          "media_urls": [
            "https://cdn.example.org/review.jpg"
          ]
        }
      ],
      "rating_distribution": [
        {
          "value": 4,
          "count": 2,
          "percent": 0.25
        }
      ],
      "review_pagination": {
        "loaded_count": 1,
        "reported_review_count": 8,
        "has_more": true,
        "complete": false
      },
      "variants_complete": false,
      "sections": {
        "description": {
          "state": "present",
          "source": "productCardMeta.description"
        },
        "images": {
          "state": "present",
          "source": "gallery.mediaItems"
        },
        "specifications": {
          "state": "present",
          "source": "widgets.AboutFullSpecsListV2"
        },
        "price": {
          "state": "present",
          "source": "price.mainPrice"
        },
        "seller": {
          "state": "present",
          "source": "shopInfo"
        },
        "delivery": {
          "state": "present",
          "source": "offerAnalytics.allDeliveryOptions"
        },
        "rating": {
          "state": "present",
          "source": "reviews.ratingStats"
        },
        "reviews": {
          "state": "present",
          "source": "reviews.reviews"
        },
        "variants": {
          "state": "unverified",
          "source": "options"
        }
      },
      "warnings": [
        "unverified_variants",
        "price_personal"
      ],
      "fetch": {
        "fetched_at": "2026-10-12T09:00:00Z",
        "request_id": "11111111-1111-4111-8111-111111111111",
        "attempts": 1,
        "cache_hit": false
      }
    }
  }
}

Частичная карточка

Отсутствующие значения сохраняются как null, пустые списки — []. Неизвестное значение не равно 0 или false. Неполный раздел не делает всю карточку ошибкой: обязательные контейнеры сохраняются.

Состояния секций: present — данные присутствуют; empty — явно пустой раздел; missing — данных нет в источнике; unverified — полнота не проверена. Для variants состояние всегда unverified.

JSON
{
  "status": "ok",
  "data": {
    "item_id": "000100000001",
    "product_url": "https://market.yandex.ru/card/example-product/000100000001",
    "title": "Синтетический чайник",
    "brand": null,
    "desc": null,
    "currency": null,
    "price_info": {
      "sale_price": null,
      "origin_price": null
    },
    "main_imgs": [
      "https://cdn.example.org/kettle.jpg"
    ],
    "main_videos": [],
    "category_path": [],
    "product_props": [
      {
        "group": "Общие",
        "name": "Объём",
        "value": "1.5 л"
      }
    ],
    "shop_info": {
      "shop_name": null,
      "shop_url": null
    },
    "delivery_info": {
      "options": [],
      "context": "anonymous_session"
    },
    "review_info": {
      "rating_star": null,
      "rating_count": null,
      "review_count": null
    },
    "sku_props": [],
    "skus": [],
    "market": {
      "schema_version": "1.0",
      "ids": {
        "osku_id": "000100000001",
        "catalog_sku_id": null,
        "product_id": null,
        "offer_id": null,
        "business_id": null,
        "shop_id": null,
        "supplier_id": null,
        "vendor_id": null,
        "category_id": null,
        "navnode_id": null
      },
      "availability": {
        "in_stock": null,
        "available_count": null
      },
      "price_context": {
        "anonymous": true,
        "is_personal": null,
        "discount_percent": null
      },
      "seller": {
        "business_id": null,
        "shop_id": null,
        "rating": null,
        "rating_count": null,
        "reviews_count": null,
        "orders_count": null,
        "subscribers_count": null,
        "age_text": null,
        "logo_url": null,
        "legal_info": []
      },
      "manufacturer": {
        "brand_name": null,
        "legal_name": null,
        "country": null
      },
      "reviews": [],
      "rating_distribution": [],
      "review_pagination": {
        "loaded_count": 0,
        "reported_review_count": null,
        "has_more": null,
        "complete": false
      },
      "variants_complete": false,
      "sections": {
        "description": {
          "state": "missing",
          "source": null
        },
        "images": {
          "state": "present",
          "source": "gallery.mediaItems"
        },
        "specifications": {
          "state": "missing",
          "source": null
        },
        "price": {
          "state": "missing",
          "source": null
        },
        "seller": {
          "state": "missing",
          "source": null
        },
        "delivery": {
          "state": "missing",
          "source": null
        },
        "rating": {
          "state": "missing",
          "source": null
        },
        "reviews": {
          "state": "empty",
          "source": "reviews.reviews"
        },
        "variants": {
          "state": "unverified",
          "source": "options"
        }
      },
      "warnings": [
        "section_missing",
        "unverified_variants"
      ],
      "fetch": {
        "fetched_at": "2026-10-12T09:00:00Z",
        "request_id": "11111111-1111-4111-8111-111111111111",
        "attempts": 1,
        "cache_hit": false
      }
    }
  }
}

Справочник всех полей

Пути ниже отсчитываются от data. Идентификаторы — строки, денежные суммы — конечные неотрицательные decimal strings без символа валюты; nullable значения допускают null. Рейтинги — 0..5, счётчики — целые ≥0. URL — безопасные HTTPS-ссылки; BHAPI не загружает возвращённые медиа. Описание и отзывы — недоверенный текст, их нельзя исполнять как HTML.

Основные поля карточки

ПолеТипСмысл
item_idstringOSKU карточки из пути URL; сохраняет ведущие нули.
product_urlstringКаноническая ссылка карточки с значимым offer/shop query.
titlestringНепустое название товара.
brandstring | nullБренд.
descstring | nullОписание как недоверенный текст.
currencystring | nullКод валюты.
main_imgsarrayИзображения карточки.
main_videosarrayВидео карточки.

price_info

ПолеТипСмысл
price_infoobjectЦена выбранного оффера.
price_info.sale_pricestring | nullТекущая цена; строка "0" допустима.
price_info.origin_pricestring | nullИсходная цена.

category_path

ПолеТипСмысл
category_patharrayПуть категорий.
category_path[].idstring | nullСтроковый идентификатор.
category_path[].namestringНазвание.
category_path[].urlstring | nullБезопасный HTTPS URL категории или null, если ссылка неизвестна.

product_props

ПолеТипСмысл
product_propsarrayХарактеристики товара.
product_props[].groupstringГруппа характеристики.
product_props[].namestringНазвание.
product_props[].valuestringЗначение.

shop_info

ПолеТипСмысл
shop_infoobjectПубличные данные магазина.
shop_info.shop_namestring | nullНазвание магазина.
shop_info.shop_urlstring | nullСсылка магазина.

delivery_info

ПолеТипСмысл
delivery_infoobjectДоставка в анонимной сессии.
delivery_info.optionsarrayНаблюдаемые варианты доставки.
delivery_info.options[].typestring | nullСпособ доставки.
delivery_info.options[].day_frominteger | nullНачало относительного интервала дней; не календарная дата.
delivery_info.options[].day_tointeger | nullКонец относительного интервала дней.
delivery_info.options[].datestring | nullТекст даты источника.
delivery_info.options[].pricestring | nullСтоимость как decimal string.
delivery_info.options[].currencystring | nullКод валюты.
delivery_info.options[].partner_typestring | nullТип партнёра доставки.
delivery_info.contextanonymous_sessionВсегда anonymous_session; адрес клиента отсутствует.

review_info

ПолеТипСмысл
review_infoobjectСводные счётчики оценок и отзывов.
review_info.rating_starnumber | nullРейтинг товара, 0..5.
review_info.rating_countinteger | nullЧисло оценок, не число текстовых отзывов.
review_info.review_countinteger | nullЧисло отзывов по источнику; не длина загруженного списка.

sku_props

ПолеТипСмысл
sku_propsarrayЯвно объявленные оси вариантов.
sku_props[].namestringНазвание.
sku_props[].valuesarrayЗначения оси.

skus

ПолеТипСмысл
skusarrayНаблюдаемые варианты без построения декартова произведения.
skus[].item_idstring | nullOSKU карточки из пути URL; сохраняет ведущие нули.
skus[].offer_idstring | nullСобственный ID оффера варианта; может отличаться от выбранного оффера основной карточки.
skus[].product_urlstring | nullСсылка варианта; допускается /product--..., может отличаться от основного OSKU.
skus[].propertiesdict[string,string]Словарь строковых значений только по объявленным sku_props[].name; значения null запрещены.
skus[].properties.ЦветstringЗначение объявленной оси варианта.
skus[].pricestring | nullСтоимость как decimal string.
skus[].currencystring | nullКод валюты.
skus[].availableboolean | nullНаличие варианта; null означает неизвестно.

market

ПолеТипСмысл
marketobjectРасширенный контракт Маркета.
market.schema_version1.0Всегда "1.0".
market.variants_completefalseВсегда false; полный набор вариантов не гарантируется.
market.warningsarrayТолько закрытые коды предупреждений, перечисленные ниже.

market.ids

ПолеТипСмысл
market.idsobjectНезависимые идентификаторы; не подменяют OSKU.
market.ids.osku_idstringОбязательный OSKU; совпадает с item_id.
market.ids.catalog_sku_idstring | nullID каталожного SKU; отличается от OSKU.
market.ids.product_idstring | nullID каталожного продукта.
market.ids.offer_idstring | nullID выбранного оффера; при do-waremd5 обязан совпасть с запросом.
market.ids.business_idstring | nullID бизнеса продавца.
market.ids.shop_idstring | nullID магазина; не подменяет business_id.
market.ids.supplier_idstring | nullID поставщика.
market.ids.vendor_idstring | nullID бренда/производителя.
market.ids.category_idstring | nullID категории.
market.ids.navnode_idstring | nullID навигационного узла.

market.availability

ПолеТипСмысл
market.availabilityobjectНаличие по источнику, без вывода из цены.
market.availability.in_stockboolean | nullНаличие товара; null неизвестно.
market.availability.available_countinteger | nullДоступное количество; null неизвестно.

market.price_context

ПолеТипСмысл
market.price_contextobjectУсловия наблюдаемой цены.
market.price_context.anonymoustrueВсегда true.
market.price_context.is_personalboolean | nullФлаг персональной цены источника; может быть true при anonymous=true.
market.price_context.discount_percentnumber | nullКонечное число 0..100; отдельная шкала скидки.

market.seller

ПолеТипСмысл
market.sellerobjectПубличная статистика продавца.
market.seller.business_idstring | nullID бизнеса продавца.
market.seller.shop_idstring | nullID магазина; не подменяет business_id.
market.seller.ratingnumber | nullРейтинг 0..5.
market.seller.rating_countinteger | nullЧисло оценок, не число текстовых отзывов.
market.seller.reviews_countinteger | nullЧисло отзывов продавца.
market.seller.orders_countinteger | nullЗаказы продавца; не являются продажами товара.
market.seller.subscribers_countinteger | nullЧисло подписчиков продавца.
market.seller.age_textstring | nullВозраст продавца как исходный текст.
market.seller.logo_urlstring | nullСсылка публичного логотипа.
ПолеТипСмысл
market.seller.legal_infoarrayТолько опубликованные реквизиты продавца.
market.seller.legal_info[].legal_formstring | nullОрганизационная форма.
market.seller.legal_info[].namestring | nullНазвание.
market.seller.legal_info[].supplier_namestring | nullНазвание поставщика.
market.seller.legal_info[].innstring | nullОпубликованный ИНН.
market.seller.legal_info[].ogrnstring | nullОпубликованный ОГРН.

market.manufacturer

ПолеТипСмысл
market.manufacturerobjectДанные производителя.
market.manufacturer.brand_namestring | nullНазвание бренда.
market.manufacturer.legal_namestring | nullЮридическое название.
market.manufacturer.countrystring | nullСтрана.

market.reviews

ПолеТипСмысл
market.reviewsarrayТолько загруженные отзывы, без профилей авторов.
market.reviews[].idstringСтроковый идентификатор.
market.reviews[].ratingnumber | nullРейтинг 0..5.
market.reviews[].date_textstring | nullДата отзыва как исходный текст.
market.reviews[].prostring | nullДостоинства как недоверенный текст.
market.reviews[].contrastring | nullНедостатки как недоверенный текст.
market.reviews[].commentstring | nullКомментарий как недоверенный текст.
market.reviews[].votes_agreeinteger | nullЧисло согласных голосов.
market.reviews[].votes_rejectinteger | nullЧисло несогласных голосов.
market.reviews[].media_urlsarrayПубличные HTTPS-ссылки медиа отзыва.

market.rating_distribution

ПолеТипСмысл
market.rating_distributionarrayРаспределение оценок в исходной шкале.
market.rating_distribution[].valueintegerЦелая оценка 1..5.
market.rating_distribution[].countinteger | nullЧисло оценок в группе.
market.rating_distribution[].percentnumber | nullКонечное неотрицательное число без гарантии шкалы: 0.25 сохраняется, не превращается в 25.

market.review_pagination

ПолеТипСмысл
market.review_paginationobjectПолнота наблюдаемых отзывов.
market.review_pagination.loaded_countintegerРовно len(market.reviews).
market.review_pagination.reported_review_countinteger | nullОбщий счётчик источника; может отличаться от review_info.review_count.
market.review_pagination.has_moreboolean | nullПризнак дополнительных отзывов; null неизвестно.
market.review_pagination.completefalseВсегда false в версии 1.0.

market.sections

ПолеТипСмысл
market.sectionsobjectДевять закрытых разделов с state/source.

market.sections.description

ПолеТипСмысл
market.sections.descriptionobjectИменованный раздел; объект state/source.
market.sections.description.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.description.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.images

ПолеТипСмысл
market.sections.imagesobjectИменованный раздел; объект state/source.
market.sections.images.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.images.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.specifications

ПолеТипСмысл
market.sections.specificationsobjectИменованный раздел; объект state/source.
market.sections.specifications.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.specifications.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.price

ПолеТипСмысл
market.sections.priceobjectСтоимость как decimal string.
market.sections.price.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.price.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.seller

ПолеТипСмысл
market.sections.sellerobjectПубличная статистика продавца.
market.sections.seller.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.seller.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.delivery

ПолеТипСмысл
market.sections.deliveryobjectИменованный раздел; объект state/source.
market.sections.delivery.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.delivery.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.rating

ПолеТипСмысл
market.sections.ratingobjectРейтинг 0..5.
market.sections.rating.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.rating.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.reviews

ПолеТипСмысл
market.sections.reviewsobjectТолько загруженные отзывы, без профилей авторов.
market.sections.reviews.statestringpresent | empty | missing | unverified; variants только unverified.
market.sections.reviews.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.sections.variants

ПолеТипСмысл
market.sections.variantsobjectИменованный раздел; объект state/source.
market.sections.variants.stateunverifiedpresent | empty | missing | unverified; variants только unverified.
market.sections.variants.sourcestring | nullЛогическая метка источника или null; допустимые значения перечислены ниже.

market.fetch

ПолеТипСмысл
market.fetchobjectДанные исходного получения и корреляции.
market.fetch.fetched_atstringUTC timestamp исходного получения.
market.fetch.request_idstringСерверный идентификатор текущей операции.
market.fetch.attemptsintegerВнутренние попытки источника; не количество списаний BHAPI.
market.fetch.cache_hitbooleanОжидается false; true нарушает принудительное обновление и даёт 502.

Допустимые source: productCardMeta.description, gallery.mediaItems, widgets.AboutFullSpecsListV2, price.mainPrice, shopInfo, offerAnalytics.allDeliveryOptions, deliveryInfoOption, reviews.ratingStats, reviews.reviews, options или null.

Коды warnings: review_count_mismatch, jsonld_conflict, unverified_variants, price_personal, section_missing. Они не гарантируют полноту или отсутствие конфликтов в источнике.

Фильтры: получить, выбрать, применить

POST/yamarket/api/v1/search/filters

Метод возвращает расширенную форму фильтров первой страницы для текущего поиска. Принимает query, sort, category_ids, price_from, price_to и filters по тем же правилам. Поля page и include_filters здесь запрещены. Ответ не содержит items и pagination.

  1. Выполните поиск с нужным query. Сохраните возвращённые category_ids и условия поиска.
  2. Для расширенной формы вызовите метод фильтров с теми же query, sort, категориями, ценой и текущим enum. Не отправляйте page/include_filters.
  3. Найдите descriptor с supported=true, type="enum" и цифровым id. Покажите пользователю name и варианты values[].label. Передавайте выбранный values[].id как value_id; названия не заменяют ID.
  4. Повторите поиск с выбранным enum, сохранив категории и остальные условия. При изменении выбора сбрасывайте page в 1.
  5. Проверьте HTTP 200, status="ok" и подтверждённые selected_filters, price_from, price_to. Если выбор не подтверждён Маркетом, BHAPI вернёт HTTP 422 с reason="filter_not_applied", а не успешную неотфильтрованную выдачу.

ID динамические: берите их из своего актуального ответа для того же контекста. ID категории, фильтра и значения — разные сущности. Примеры ниже синтетические, их ID не являются постоянным справочником.

id="glprice" с supported=true означает поддержку цены через отдельные price_from/price_to. Передавать glprice внутри filters нельзя. Специальные range/boolean/radio и другие неподдерживаемые фильтры могут отображаться с supported=false: наличие на сайте не означает поддержку на входе API. Диапазоны характеристик, фильтры доставки/оплаты и несколько enum пока не принимаются.

Для сброса enum передайте filters: [] или опустите поле. Для сброса цены опустите границу либо задайте null. Начинайте с первой страницы. Сохраняйте категории, чтобы продолжить тот же поиск. Сервер не хранит активный выбор между запросами: для каждой страницы передавайте все условия снова.

Как читать поисковый ответ

Успех: {"status":"ok","data":SearchData}. Данные находятся непосредственно в data, без вложенных code, msg или второго data. Поля ниже указаны относительно data.

Контекст и товары
ПолеТипСмысл
querystringПодтверждённый текст текущего поиска после trim.
sort, sort_optionsstringОба содержат активный код сортировки, например popular. sort_options — строка, не список доступных сортировок.
category_idsstring[]Категории текущей выдачи. [] — категории не опубликованы; это не отсутствие категории у всех товаров.
selected_filtersobject[]Подтверждённый выбор: 0–1 объект с полями id и value_id.
price_from, price_tonumber | nullПодтверждённые выбранные границы, не диапазон фактических цен товаров.
itemsobject[]Одна страница сниппетов в порядке Маркета. Одинаковые канонические URL удаляются; разные офферные контексты могут сохраниться.
items[].item_idstringOSKU из /card/…, не каталоговый SKU/model ID.
items[].titlestringНепустое название текущего сниппета.
items[].product_urlstringHTTPS-ссылка товара; tracking удалён, допустимый контекст оффера/магазина сохранён.
items[].pricenumber | nullПоказанная цена текущего сниппета, например 985.45. null — неизвестно, не 0.
items[].currencystring | nullRUB при распознанном рублёвом обозначении, не выводится из границ цены.
items[].image_urlstring | nullURL картинки сниппета, без скачивания изображения.
items[].ratingnumber | nullРейтинг товара 0–5, не магазина.
items[].price_typestring | nullyandex_pay — явно указана цена с картой Яндекс Пэй; displayed — без такого обозначения; null — цена не распознана.
items[].shop_namestring | nullНазвание магазина, если опубликовано.

Цена относится к анонимной сессии. Доступность цены с картой Яндекс Пэй конкретному покупателю не гарантируется; цену без Пэй и скидку по промокоду API не рассчитывает. Счётчики отзывов, оценок и покупок, тексты цены и доставки в поисковых товарах не возвращаются. Для подробностей используйте отдельный запрос карточки, который расходует свою единицу квоты.

Пагинация и сортировка

pagination — только поиск товаров
ПолеТипСмысл
pagination.pageintegerПодтверждённая фактическая страница; совпадает с запрошенной.
pagination.items_per_pageinteger | nullРазмер страницы по Маркету; не обязан совпадать с len(items).
pagination.totalinteger | nullТекущая оценка количества результатов, не аудит уникальных SKU.
pagination.page_countinteger | nullЧисло страниц источника, без собственного пересчёта.
pagination.has_next_pageboolean | nulltrue — следующая страница есть; false — нет; null — неизвестно.

Для следующей страницы меняйте только page. При смене запроса, сортировки, категорий, enum или цены начинайте с page=1. Размер страниц непостоянный; не рассчитывайте offset или page_count из фиксированного размера. Отсутствующие/некорректные необязательные счётчики становятся null, а не нулём. Подтверждённая пустая выдача — успешный ответ с items=[] и total=0.

Сортирует Маркет, API сохраняет порядок сниппетов. Реклама, особые цены и изменения ассортимента влияют на порядок: строгая монотонность price/rating не обещается. Между запросами порядок, товары и totals могут измениться; snapshot/cursor нет. При обходе страниц учитывайте возможные повторы/пропуски и не считайте полученный список полным каталогом.

Дескрипторы фильтров

filters[] — метаданные доступного выбора
ПолеТипСмысл
filters[].idstringID фильтра. У специальных неподдерживаемых фильтров может быть нецифровым.
filters[].namestringНазвание; пустая строка при отсутствии.
filters[].typestringenum/range/boolean/radio и другие; unknown, если тип отсутствует.
filters[].unitstring | nullЕдиница измерения, например ₽; null при отсутствии.
filters[].supportedbooleanМожно применить цифровой enum через filters или glprice через отдельные границы цены.
filters[].valuesobject[]Опубликованные варианты текущего поиска, без гарантии полного списка.
filters[].values[].idstringID варианта; для входного enum только 1–32 цифры.
filters[].values[].labelstringПодпись варианта; может быть пустой.
filters[].values[].selectedbooleanВариант отмечен выбранным в текущем контексте.
filters[].values[].foundinteger | nullСчётчик источника для варианта; не общий total и не гарантия уникальных товаров.
filters[].selected_value_idsstring[]Текущий выбор источника; специальные фильтры могут содержать нецифровые ID.
filters[].lazyboolean | nullТолько /search: признак ленивой загрузки; null — неизвестно.
filters[].loadedboolean | nullТолько /search: признак загрузки списка; true не доказывает полноту.
filters[].completebooleanТолько /search: всегда false, полнота не доказана.

В /search/filters у дескрипторов отсутствуют lazy, loaded и complete. Расширенная форма и непустой values не означают полный список. В обоих поисковых методах нет служебных warnings/fetch. При include_filters=false в /search ключ filters полностью исключён, а не заменён null или [].

Синтетический успешный ответ с одной позицией; это пример формы, не обещание товара, цены или ID:

JSON
{
  "status": "ok",
  "data": {
    "query": "coffee",
    "sort": "popular",
    "category_ids": [
      "123"
    ],
    "selected_filters": [],
    "price_from": null,
    "price_to": null,
    "sort_options": "popular",
    "pagination": {
      "page": 1,
      "items_per_page": 8,
      "total": 17,
      "page_count": 2,
      "has_next_page": true
    },
    "items": [
      {
        "item_id": "123",
        "title": "Coffee",
        "product_url": "https://market.yandex.ru/card/test/123?do-waremd5=offer123",
        "price": 1303.0,
        "currency": "RUB",
        "image_url": "https://avatars.mds.yandex.net/get-mpic/123/example/orig",
        "rating": 4.8,
        "price_type": "yandex_pay",
        "shop_name": "Example shop"
      }
    ],
    "filters": [
      {
        "id": "7893318",
        "name": "Brand",
        "type": "enum",
        "unit": null,
        "supported": true,
        "values": [
          {
            "id": "456",
            "label": "Example brand",
            "selected": false,
            "found": null
          }
        ],
        "selected_value_ids": [],
        "lazy": true,
        "loaded": false,
        "complete": false
      },
      {
        "id": "glprice",
        "name": "Price",
        "type": "range",
        "unit": "RUB",
        "supported": true,
        "values": [],
        "selected_value_ids": [],
        "lazy": null,
        "loaded": null,
        "complete": false
      }
    ]
  }
}

Пример ответа метода фильтров для того же контекста:

JSON
{
  "status": "ok",
  "data": {
    "query": "coffee",
    "sort": "popular",
    "category_ids": [
      "123"
    ],
    "selected_filters": [],
    "price_from": null,
    "price_to": null,
    "sort_options": "popular",
    "filters": [
      {
        "id": "7893318",
        "name": "Brand",
        "type": "enum",
        "unit": null,
        "supported": true,
        "values": [
          {
            "id": "456",
            "label": "Example brand",
            "selected": false,
            "found": null
          }
        ],
        "selected_value_ids": []
      },
      {
        "id": "glprice",
        "name": "Price",
        "type": "range",
        "unit": "RUB",
        "supported": true,
        "values": [],
        "selected_value_ids": []
      }
    ]
  }
}

При ошибке фильтра публичный ответ имеет нейтральную форму; внутренние filter_ids и текст источника не передаются:

HTTP 422
{"detail":{"code":422,"reason":"filter_not_applied","message":"Применение выбранного фильтра или цены не подтверждено."}}

Примеры: от поиска до применения фильтра

cURL (Bash): первый запрос получает категории, второй — расширенную форму. Третий использует только синтетические ID: замените их ID из своего ответа. В PowerShell используйте curl.exe и UTF-8 JSON-файл через --data-binary @request.json, чтобы сохранить кавычки и кириллицу. Не сохраняйте токен в общем файле.

cURL
curl --max-time 180 --fail-with-body -i -X POST "https://bhapi.ru/yamarket/api/v1/search" \
  -H "X-API-Token: $BHAPI_TOKEN" -H "Content-Type: application/json" \
  --data '{"query":"кофе","sort":"popular","page":1}'

# Замените category_ids категориями из первого ответа.
curl --max-time 180 --fail-with-body -i -X POST "https://bhapi.ru/yamarket/api/v1/search/filters" \
  -H "X-API-Token: $BHAPI_TOKEN" -H "Content-Type: application/json" \
  --data '{"query":"кофе","sort":"popular","category_ids":["123"]}'

# Замените все ID актуальными; синтетический пример применения enum + цены.
curl --max-time 180 --fail-with-body -i -X POST "https://bhapi.ru/yamarket/api/v1/search" \
  -H "X-API-Token: $BHAPI_TOKEN" -H "Content-Type: application/json" \
  --data '{"query":"кофе","sort":"price_asc","category_ids":["123"],"page":1,"price_from":100,"price_to":2000,"filters":[{"id":"7893318","value_id":"456"}],"include_filters":false}' 

Python: выберите нужный вариант осмысленно по name/label. Пример показывает варианты и запрашивает ID у оператора, проверяет их наличие и поддержку. Каждый успешный вызов, включая discovery, расходует единицу квоты; получение второй страницы — ещё один вызов.

Python
import os
import requests

BASE = "https://bhapi.ru/yamarket/api/v1"
HEADERS = {"X-API-Token": os.environ["BHAPI_TOKEN"]}

def call(method, params):
    response = requests.post(f"{BASE}/{method}", headers=HEADERS,
                             json=params, timeout=180)
    if response.status_code != 200:
        print("HTTP", response.status_code, "Retry-After:",
              response.headers.get("Retry-After"), "X-Request-ID:",
              response.headers.get("X-Request-ID"))
        print(response.json().get("detail"))
        response.raise_for_status()
    result = response.json()
    if result.get("status") != "ok" or not isinstance(result.get("data"), dict):
        raise RuntimeError("Неожиданный формат ответа")
    return result["data"]

context = {"query": "кофе", "sort": "popular"}
first = call("search", {**context, "page": 1})
context["category_ids"] = first["category_ids"]
form = call("search/filters", context)
choices = {}
for descriptor in form["filters"]:
    if (descriptor["supported"] and descriptor["type"] == "enum"
            and descriptor["id"].isascii() and descriptor["id"].isdigit()):
        for value in descriptor["values"]:
            if value["id"].isascii() and value["id"].isdigit():
                choices[(descriptor["id"], value["id"])] = value
                print(descriptor["id"], descriptor["name"],
                      value["id"], value["label"])
if not choices:
    raise RuntimeError("Подходящий enum в этом контексте не опубликован")
filter_id = input("ID нужного фильтра: ").strip()
value_id = input("ID нужного значения: ").strip()
if (filter_id, value_id) not in choices:
    raise ValueError("Выберите опубликованные ID из списка выше")
selection = [{"id": filter_id, "value_id": value_id}]
params = {**context, "filters": selection, "price_from": 100,
          "price_to": 2000, "include_filters": False, "page": 1}
result = call("search", params)
if (result["selected_filters"] != selection or result["price_from"] != 100
        or result["price_to"] != 2000 or result["pagination"]["page"] != 1):
    raise RuntimeError("Условия ответа не совпадают с запросом")
for item in result["items"]:
    print(item["title"], item["price"], item["price_type"], item["product_url"])
# Никакого фиксированного размера страниц и автоматического обхода всего каталога.
if result["pagination"]["has_next_page"] is True:
    second = call("search", {**params, "page": 2})
    print("Вторая страница:", len(second["items"]))

Не выполняйте автоматический повтор после сетевого timeout: завершение операции и списание могут быть уже подтверждены. Сверьте кабинет и X-Request-ID. При HTTP 422 filter_not_applied обновите доступные фильтры в том же контексте и пересмотрите выбор; не используйте ответ как отфильтрованную выдачу. При 429 проверьте Retry-After, ограничения и оставшуюся квоту.

Правила URL

Принимается HTTPS URL https://market.yandex.ru/card/<slug>/<osku_id>, порт 443 допустим. OSKU — строка с сохранением ведущих нулей, а не catalog_sku_id. Во входе метода карточки короткие ссылки, URL каталога/поиска, редиректы и пути /product--... не поддерживаются. В ссылках возвращённых SKU путь /product--... допустим: это ссылки вариантов, а не вход основного метода.

Сохраняются параметры выбора оффера/магазина: do-waremd5, resale_goods, businessId, shopId, isShopInShop. Значения непустые, до 512 символов; дубликаты этих ключей запрещены, всего не более 100 query-полей. Tracking и fragment удаляются. Без do-waremd5 возвращается выбранный источником оффер, без гарантии минимальной цены.

Дополнительные поля JSON и неизвестные query-поля метода дают 422.

Ошибки

Таблицы и примеры этого раздела описывают ошибки legacy (формат по умолчанию). Чтобы получать единый контракт ошибок, передайте заголовок X-BHAPI-Error-Format: problem-v1. Статусы некоторых ошибок в этом режиме отличаются; успешные ответы не меняются. Общий справочник кодов, форматов и действий клиента →

Нейтральная ошибка · 502
{"detail":{"code":502,"reason":"upstream_invalid_response","message":"Сервис Яндекс.Маркет вернул некорректный ответ."}}

Код HTTP — категория ошибки; reason, если присутствует, уточняет причину. В ошибках авторизации, локальных лимитов и общем ответе 500 формат detail может отличаться от примера.

Публичные HTTP-ошибки
КодПричина
400query_token_deprecated: токен в параметре URL не принимается; используйте заголовок X-API-Token.
401/403Токен отсутствует, отозван или нет доступа к парсеру.
404item_not_found: товар не найден; это подтверждённый ответ о товаре.
413request_too_large: тело запроса больше 16 КиБ.
415unsupported_media_type: нужен Content-Type: application/json.
417item_fetch_failed: не удалось подтвердить выбранное предложение товара.
422invalid_request: проверьте поля и типы выбранного метода; для карточки — поле url и допустимый путь.
filter_not_applied: выбранный enum или цена не подтверждены. Обновите фильтры и проверьте контекст поиска.
429Локальный лимит токена или квота тарифа: проверьте кабинет.
Ограничение частоты по IP: снизьте общую частоту запросов с этого IP.
parser_overloaded: очередь или ограничение источника. Если есть Retry-After, выдержите указанную паузу; не повторяйте автоматически.
500Неожиданная ошибка BHAPI; reason может содержать internal_error или отсутствовать. Передайте X-Request-ID и время запроса в поддержку.
502upstream_invalid_response: ответ не прошёл проверку формата или соответствия запрошенному товару.
503upstream_unavailable: парсер выключен, не настроен либо источник временно недоступен.
upstream_unreachable: ошибка соединения с источником.
upstream_access_restricted: источник ограничил доступ; это не ошибка API-токена клиента BHAPI.
parser_internal_error: внутренняя ошибка парсера.
504upstream_timeout: истекло время ожидания операции или чтения ответа.

Для parser_overloaded заголовок Retry-After — целые секунды 1..3600, по умолчанию 1; Cache-Control: no-store. Для item_not_found также no-store. Локальные лимиты и квоты с кодом 429 сохраняют собственную политику заголовков. Исходные технические сообщения не возвращаются.

Квота и ограничения

Один успешный запрос любого метода расходует одну единицу квоты Яндекс.Маркет: карточка, страница поиска (включая пустую выдачу) или получение фильтров. Отдельного списания за каждый товар в items нет; каждый дополнительный вызов карточки или страницы — новая единица. Ошибка парсинга или отмена до подтверждения результата не расходует успешную квоту. Лимиты и доступ берутся из назначенного тарифа в кабинете.

Если результат уже подтверждён, сбой журнала или отмена после подтверждения может привести к ошибке ответа и сохранённому однократному списанию. Автоматический возврат не гарантируется. Для сверки передайте в поддержку X-Request-ID из заголовков ответа и время запроса; не отправляйте запрос повторно автоматически при неясном исходе.

Поиск возвращает одну страницу за запрос. Batch и метод карточки по ID не поддерживаются. Полный набор отзывов и вариантов не гарантируется: review_pagination.complete и variants_complete всегда false. rating_count — оценки, review_count — отзывы; loaded_count — только загруженные отзывы. seller.orders_count относится к продавцу и не является продажами товара. Округлённые счётчики нельзя преобразовывать в точные числа.

Цена, наличие и доставка относятся к анонимной сессии (anonymous_session). Регион клиента, персональная цена и минимальная цена не гарантируются. fetched_at/cache_hit не гарантируют актуальность цены на стороне самого Маркета. Nullable наличие нельзя выводить из цены.

Пример Python

Python
import requests

response = requests.post(
    "https://bhapi.ru/yamarket/api/v1/item/detail-by-url",
    headers={"X-API-Token": "ВАШ_API_TOKEN"},
    json={"url": "https://market.yandex.ru/card/example-product/000100000001"},
    timeout=180,
)
if response.status_code == 429:
    print("Пауза, секунд:", response.headers.get("Retry-After"))
response.raise_for_status()
price = response.json()["data"]["price_info"]["sale_price"]
print("Цена неизвестна" if price is None else price)

Пример JavaScript

Только для собственного доверенного приложения: токен нельзя размещать в публичном frontend. Общедоступный сайт вызывает API через свой backend.

JavaScript
const response = await fetch("https://bhapi.ru/yamarket/api/v1/item/detail-by-url", {
  method: "POST",
  headers: {"Content-Type": "application/json", "X-API-Token": "ВАШ_API_TOKEN"},
  body: JSON.stringify({url: "https://market.yandex.ru/card/example-product/000100000001"}),
  signal: AbortSignal.timeout(180000)
});
if (response.status === 429) console.log("Пауза, секунд:", response.headers.get("Retry-After"));
if (!response.ok) throw new Error("Ошибка API: " + response.status);
const price = (await response.json()).data.price_info.sale_price;
console.log(price === null ? "Цена неизвестна" : price);

Пример PHP

PHP
<?php
$retryAfter = null;
$ch = curl_init("https://bhapi.ru/yamarket/api/v1/item/detail-by-url");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json", "X-API-Token: ВАШ_API_TOKEN"],
    CURLOPT_POSTFIELDS => json_encode(["url" => "https://market.yandex.ru/card/example-product/000100000001"], JSON_THROW_ON_ERROR),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 180,
    CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$retryAfter) {
        if (stripos($line, "Retry-After:") === 0) $retryAfter = trim(substr($line, 12));
        return strlen($line);
    }
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 429) echo "Пауза, секунд: " . ($retryAfter ?? "не указана");
if ($body === false || $status !== 200) throw new RuntimeException("Ошибка API: " . $status);
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
echo $data["data"]["price_info"]["sale_price"] ?? "Цена неизвестна";