Синхронизация данных
Синхронизация нужна, когда отзывы живут не только у нас, но и в вашей базе: в CRM, в
хранилище, в собственной аналитике. Одна ручка — GET /v2/reviews/sync.
Если своей базы нет, эта страница вам не нужна: показать отзывы на сайте умеет лента, узнать о новом — вебхук.
Цикл
cursor = db.load_cursor() # None при первом запуске
params = {"cursor": cursor} if cursor else {"group_id": 20, "limit": 500}
while True:
page = httpx.get(f"{API}/reviews/sync", headers=H, params=params).json()
for review in page["data"]:
db.upsert_review(review) # склейка по review["id"]
db.save_cursor(page["next_cursor"]) # ПОСЛЕ каждой страницы
if not page["has_next"]:
break
params = {"cursor": page["next_cursor"]}Первый прогон выгружает всю историю, следующие — только изменения. Возвращайтесь с сохранённым курсором раз в 5–15 минут.
Четыре правила
1. Курсор хранится в базе, а не в памяти процесса. В нём весь смысл: завтрашний запуск продолжает с той же точки, а не выкачивает всё заново.
2. Сохранять после каждой страницы. Тогда обрыв связи стоит одной страницы, а не всего обхода.
3. В режиме all — UPSERT по id, не INSERT. Изменённый отзыв приходит со старым
id: новой строки мы не заводим, поэтому вставка даст дубли или ошибку уникальности.
В режиме new достаточно INSERT — там ничего не приходит дважды. Потерять отзыв поток
не может ни в том, ни в другом режиме, это его единственная твёрдая гарантия.
4. next_cursor приходит всегда — и на последней странице, и на пустой. Признак
«догнал» — это has_next: false, а не отсутствие курсора.
Что приходит
Изменением считается: правка текста или оценки на площадке, появившийся или изменившийся
ответ организации, изменившийся набор фотографий, пропажа отзыва с площадки
(is_deleted: true) и его возврат. Пересбор сам по себе — не изменение: если на площадке
ничего не поменялось, отзыв в поток не попадёт.
mode | Что приходит | Как класть к себе |
|---|---|---|
all (по умолчанию) | новые отзывы и изменения | UPSERT по id |
new | только впервые появившиеся | INSERT: каждый приходит один раз за всю жизнь |
Режим запечён в курсор — для второго режима заведите второй курсор.
Отдельного режима «только изменения» нет, и он не нужен: когда вы догнали текущее
состояние в режиме all, дальше в нём приходят как раз одни изменения — новых-то
пока не появилось.
Границы
- Порядок — по времени изменения, не по дате отзыва. Отзыв 2019 года, у которого вчера появился ответ, придёт после вчерашнего. Сортировать нечем и незачем: это поток, а не витрина.
- Фильтров нет. Отзыв, который правкой перестал подходить под фильтр, пришлось бы присылать событием «выбыл» — такого события не существует, поэтому любой фильтр сделал бы поток дырявым. Отбирайте у себя.
- Состав объекта не отслеживается. Убрали ссылку или выключили объект — отзывы по нему
просто перестанут приходить, события об этом не будет. Чистите у себя по
link_id. Обратный случай тоже стоит держать в голове: если ссылку вернули, её старые отзывы заново потоком не поедут — курсор их давно прошёл. После возврата ссылки начните обход с нуля, без сохранённого курсора. - Скрытые и закреплённые приходят обычными строками: это настройка витрины, а не свойство отзыва.
- Задержка несколько секунд. Не запаздывание сбора, а защита: отзыв, который прямо сейчас записывается, иначе оказался бы позади вашего курсора и не пришёл бы никогда.
Если курсор не принят
400 invalid_cursor — курсор испорчен, изменён или выдан другому аккаунту: начните обход
заново, без cursor. 400 cursor_mismatch — вместе с курсором передан другой group_id,
folder_id или mode: либо не передавайте их вовсе, либо передайте те же.
Полный список параметров и полей — в справочнике метода.