Синхронизация отзывов
Поток изменений: отзывы, которые появились или изменились у нас после точки, на которой вы остановились. Нужен там, где у вас своя база и её надо держать в актуальном состоянии.
Обычная лента отзывов отвечает на вопрос «покажи»,
синхронизация — на вопрос «догони, ничего не потеряв». Поэтому здесь нет ни
фильтров, ни сортировки, ни offset.
Параметры
cursorстрокаКурсор из next_cursor предыдущего ответа. С ним остальные параметры можно не передавать.
modeстрокаЧто присылать: all — новые и изменения, new — только впервые появившиеся отзывы.
limitчисло1–1000Размер страницы, 1–1000. Запекается в курсор, дальше его можно не повторять.
object_id · folder_idчислоОбъект или папка целиком. В запросе должно быть ровно одно из двух — но только на первом запросе, дальше адресат едет внутри курсора.
Ответ
dataсписок объектовОтзывы, изменившиеся после вашей позиции.
data[].idчислоИдентификатор отзыва в Rewio. По нему и склеивайте у себя.
data[].link_idчислоСсылка, с которой собран отзыв.
data[].source_idчислоПлощадка, с которой собран отзыв.
data[].authorстрокаможет быть пустымИмя автора так, как его показывает площадка. Фамилии маскируются.
data[].textстрокаможет быть пустымТекст отзыва.
data[].review_dateдата и времяможет быть пустымКогда отзыв опубликован, по времени площадки.
data[].rating_originalстрокаможет быть пустымОценка в шкале самой площадки, как есть.
data[].ratingчисломожет быть пустымТа же оценка, приведённая к шкале 1–5.
data[].review_urlстрокаможет быть пустымПрямая ссылка на отзыв на площадке.
data[].reply_textстрокаможет быть пустымОтвет организации на отзыв.
data[].reply_dateдата и времяможет быть пустымКогда организация ответила.
data[].is_deletedда/нетОтзыв пропал с площадки. В синхронизации такие приходят всегда.
data[].is_hiddenда/нетОтзыв скрыт в рамках объекта (приходит только когда true).
data[].is_pinnedда/нетОтзыв закреплён в рамках объекта (приходит только когда true).
data[].pin_positionчисломожет быть пустымПозиция закреплённого отзыва: меньше — выше.
data[].prosстрокаможет быть пустымБлок «достоинства» (только при split_pros_cons=true).
data[].consстрокаможет быть пустымБлок «недостатки» (только при split_pros_cons=true).
next_cursorстрокаПозиция, с которой продолжить. Приходит всегда — сохраните её у себя.
has_nextда/нетЕсть ли ещё изменения прямо сейчас.
accessобъектможет быть пустымОграничение глубины выдачи. Приходит, только пока оно действует.
access.reasonстрокаПочему выдача ограничена. trial — пробный доступ, manual — ограничение, согласованное по вашему договору.
access.limit_per_linkчислоСколько последних отзывов показывается по КАЖДОЙ ссылке (площадке объекта).
access.hidden_totalчислоСколько отзывов уже собрано сверх показанных. Считается по всему архиву и не зависит от фильтров запроса.
access.messageстрокаГотовая формулировка для показа человеку.
Ещё 14 полей – реже нужны
data[].has_imagesда/нетЕсть ли у отзыва изображения.
data[].imagesсписок объектовИзображения отзыва.
data[].images[].template_urlстрокаURL изображения на CDN площадки в исходном виде. У части площадок содержит шаблон размера ({size} или {width}/{height}) и потому не предназначен для прямой вставки — для показа берите preview или image.
data[].images[].previewстрокаможет быть пустымГотовый URL уменьшенной версии (превью) для списков и сеток.
data[].images[].imageстрокаможет быть пустымГотовый URL полноразмерной версии для просмотра.
data[].has_videosда/нетЕсть ли у отзыва видео.
data[].videosсписок объектовВидео отзыва.
data[].videos[].kindстрокаembed / file / hlsТип вложения: embed — встраиваемый плеер площадки (открывать в sandbox-iframe), file — прямая ссылка на видеофайл, hls — HLS-поток (.m3u8).
data[].videos[].video_urlстрокаможет быть пустымURL видео или встраиваемого плеера в зависимости от kind.
data[].videos[].thumbnailстрокаможет быть пустымURL кадра-заставки (превью) видео.
data[].videos[].widthчисломожет быть пустымШирина видео в пикселях, если известна.
data[].videos[].heightчисломожет быть пустымВысота видео в пикселях, если известна.
data[].videos[].duration_msчисломожет быть пустымДлительность видео в миллисекундах, если известна.
data[].deleted_atдата и времяможет быть пустымКогда мы заметили пропажу.
curl -X GET "https://example.com/v3/reviews/sync?object_id=20"{ "data": [ { "id": 143046, "link_id": 177, "source_id": 1, "author": "Сергей", "text": "Замечательно. Приеду ещё. ", "review_date": "2026-07-21T18:07:03", "rating_original": "5", "rating": 5, "review_url": "https://yandex.ru/maps/org/1020542995/reviews?reviews[publicId]=u182gzp1r9rg4urew906ky9dd0", "reply_text": "Уважаемый Гость,\nБлагодарим Вас за визит в гостиницу «Националь» и высокую оценку нашего обслуживания. Ждем Вас снова!\nС наилучшими пожеланиями,\t\nЕлена Позолотина\nДиректор по операционной деятельности.\n", "reply_date": "2026-07-23T13:27:07", "has_images": true, "images": [ { "template_url": "https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/{size}", "preview": "https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/M", "image": "https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/XXXL" }, { "template_url": "https://avatars.mds.yandex.net/get-altay/18473509/2a0000019f85dc42d3b7bbf4e10e375c612e/{size}", "preview": "https://avatars.mds.yandex.net/get-altay/18473509/2a0000019f85dc42d3b7bbf4e10e375c612e/M", "image": "https://avatars.mds.yandex.net/get-altay/18473509/2a0000019f85dc42d3b7bbf4e10e375c612e/XXXL" }, { "template_url": "https://avatars.mds.yandex.net/get-altay/20191917/2a0000019f85dc255b9c227cfbc55fc57724/{size}", "preview": "https://avatars.mds.yandex.net/get-altay/20191917/2a0000019f85dc255b9c227cfbc55fc57724/M", "image": "https://avatars.mds.yandex.net/get-altay/20191917/2a0000019f85dc255b9c227cfbc55fc57724/XXXL" } ], "has_videos": false, "videos": [] }, { "id": 143051, "link_id": 177, "source_id": 1, "author": "Марина", "text": "Номер не соответствовал описанию.", "review_date": "2026-07-19T09:14:00", "rating_original": "2", "rating": 2, "review_url": "https://yandex.ru/maps/org/1020542995/reviews?reviews[publicId]=u182gzp1r9rg4urew906ky9dd0", "has_images": false, "images": [], "has_videos": false, "videos": [], "is_deleted": true, "deleted_at": "2026-08-23T04:12:07" } ], "next_cursor": "eyJhIjoiMjAyNi0wOC0yM1QxNDowMzoxMSIsImQiOjE0MzA1MSwiaSI6MjAsImwiOjEwMCwibSI6ImFsbCIsInQiOiJnIiwidSI6MzAsInYiOjF9.k3Qw9x0nJ8Zr5wQfB2vLZA", "has_next": true}Как это работает
Первый запрос — с адресом объекта, дальше достаточно одного cursor:
GET /v3/reviews/sync?object_id=20&limit=200
→ 200 отзывов, next_cursor: "eyJ…", has_next: true
GET /v3/reviews/sync?cursor=eyJ…
→ следующие 200, next_cursor: "eyJ…", has_next: true
…
→ has_next: false — вы догнали текущее состояниеСохраните последний next_cursor у себя и вернитесь с ним завтра — придёт
только то, что изменилось за сутки.
cursor = load_cursor() # None при первом запуске
params = {"cursor": cursor} if cursor else {"object_id": 20, "limit": 200}
while True:
page = get("/v3/reviews/sync", params=params).json()
for review in page["data"]:
upsert(review) # склейка по review["id"]
cursor = page["next_cursor"]
save_cursor(cursor) # сохраняем ПОСЛЕ каждой страницы
if not page["has_next"]:
break
params = {"cursor": cursor}Курсор — это закладка, а не сохранённая выдача: он не устаревает и его можно хранить сколько угодно. Обрыв связи стоит одной страницы, а не всего обхода. Внутрь заглядывать не нужно и не получится — устройство курсора мы оставляем за собой, чтобы менять его, не ломая вашу интеграцию.
Один отзыв может прийти дважды
Если отзыв изменится, пока вы читаете страницы, он придёт ещё раз — уже с новым
содержимым и с тем же id: новой строки мы не заводим. Поэтому в режиме
all кладите к себе UPSERT по id, а не INSERT — иначе дубли или ошибка
уникальности. В режиме new достаточно INSERT: там ничего не приходит дважды.
Потерять отзыв поток не может: это и есть его единственная твёрдая гарантия.
Что считается изменением
Правка текста или оценки на площадке, появившийся или изменившийся ответ
организации, изменившийся набор фотографий и видео, пропажа отзыва с площадки
(is_deleted: true) и обратное восстановление. Пересбор сам по себе изменением
не считается: если на площадке ничего не поменялось, отзыв в поток не попадёт.
Два режима
mode | Что приходит | Повторы | Как класть к себе |
|---|---|---|---|
all (по умолчанию) | новые отзывы и изменения | возможны | UPSERT по id |
new | только впервые появившиеся отзывы | нет | INSERT |
Режим запекается в курсор: курсор одного режима не подходит другому — у них разные оси. Если нужны оба потока сразу, держите два курсора, это нормально.
mode=new удобен, когда отзывы уходят в CRM или в оповещения: правки и пропажи
вас не интересуют, апсерт не нужен, каждая строка приходит ровно один раз.
Первый обход — всегда с начала истории
Переключателя «начать с сегодняшнего дня» нет: первый запрос отдаёт самые давние записи, и обход идёт до конца. Синхронизация — это сперва полная копия, потом приращения.
Если полная копия вам не нужна и интересны только новые отзывы по мере появления, это не эта ручка, а вебхуки: они и присылают только новое, без архива.
Свежесть
Изменения появляются в потоке с задержкой в несколько секунд. Это не запаздывание сбора, а защита от потери: отзыв, который в этот момент записывается, иначе оказался бы позади вашего курсора и не пришёл бы никогда.
Опрашивать чаще раза в минуту смысла нет — отзывы на площадках так быстро не появляются. Практичный режим для актуальной копии: раз в 5–15 минут.
Границы
- Синхронизация не следит за составом объекта. Убрали ссылку из объекта,
выключили объект или перенесли его — отзывы по нему просто перестанут
приходить, отдельного события об этом не будет. Такие строки чистите у себя
по
link_id. Если ссылку потом вернули, её старые отзывы заново не поедут: курсор идёт по времени изменения, а возврат ссылки ничего в отзывах не меняет. Начните для этого объекта обход с нуля, без сохранённого курсора. - Скрытые и закреплённые отзывы приходят обычными строками, без флагов
is_hiddenиis_pinned: скрытие и закрепление — настройка витрины объекта, а не свойство отзыва, и в поток изменений они не попадают. - Ограниченная выдача работает здесь ровно так же, как в ленте: не больше
access.limit_per_linkпоследних отзывов на каждую площадку объекта (на пробном доступе — 20), и в ответе появляется блокaccess. Пропажи отзывов, оставшихся за этой границей, в поток не попадают.
Ошибки курсора
| Статус | code | Когда |
|---|---|---|
400 | invalid_cursor | Курсор не читается: испорчен, изменён или выдан другому аккаунту. Начните обход заново, без cursor. |
400 | cursor_mismatch | Вместе с курсором передан object_id, folder_id или mode, не совпадающий с тем, для которого курсор выдан. Либо не передавайте их, либо передайте те же. |
Остальные коды общие для всего API и описаны на странице Ошибки.