Перейти к основному содержанию
Бизнес и процессы3 мин чтения

Вебхуки или опрос API: как системы узнают об изменениях

Вебхуки быстрее, но теряются. Опрос надёжнее, но отстаёт и грузит систему. Разбираем, когда какая модель уместна и как их сочетать.

Две схемы обмена: система отправляет уведомление и система опрашивает API

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

Сравнение по существу

Вебхуки и опрос API: чем отличаются на практике
СвойствоВебхукиОпрос API
ЗадержкаСекундыПоловина интервала опроса
НагрузкаПропорциональна числу событийПостоянная, даже когда изменений нет
НадёжностьСобытие теряется при недоступности получателяПропущенное подхватится следующим циклом
Сложность приёмаНужен публичный адрес, подпись, защита от повторовДостаточно расписания и метки времени
Поведение при сбоеТребует повторов на стороне источникаСамовосстанавливается

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

Как принимать вебхуки правильно

  • Проверять подпись: без неё эндпоинт открыт для любого, кто узнал адрес
  • Отвечать быстро и обрабатывать асинхронно: источники обычно ждут ответ не более 5–10 секунд
  • Считать доставку возможной дважды: обработчик должен быть идемпотентным
  • Логировать каждое поступление вместе с телом запроса
  • Не полагаться на порядок: события приходят не в той последовательности, в которой произошли
Приём вебхука: подпись, защита от повторной обработки, быстрый ответ
export async function POST(request: Request) {
  const raw = await request.text()

  if (!verifyHmac(raw, request.headers.get("x-signature"))) {
    return new Response("Invalid signature", { status: 401 })
  }

  const event = JSON.parse(raw) as { id: string; type: string }

  // Уникальный индекс по event_id: повторная доставка не создаст
  // вторую обработку, а вернёт тот же успешный ответ
  const inserted = await db
    .insert(webhookEvents)
    .values({ eventId: event.id, type: event.type, payload: raw })
    .onConflictDoNothing({ target: webhookEvents.eventId })
    .returning({ id: webhookEvents.id })

  if (inserted.length > 0) {
    await queue.enqueue("webhook.handle", { id: inserted[0].id })
  }

  return Response.json({ ok: true })
}

Гибридная схема

На ответственных интеграциях мы используем оба механизма одновременно. Вебхуки дают скорость, а редкий сверочный опрос закрывает пропуски. Такая схема стоит немного дороже, зато снимает целый класс жалоб вида «у нас в системе есть, а у вас нет».

Сверка раз в час: догоняем то, что не пришло вебхуком
// Раз в час запрашиваем изменения за период с запасом:
// перекрытие важнее экономии запросов
export async function reconcile() {
  const since = minutesAgo(90)
  const remote = await api.listChanges({ since })

  let restored = 0
  for (const item of remote) {
    const known = await db.query.orders.findFirst({
      where: eq(orders.externalId, item.id),
    })
    if (!known || known.externalUpdatedAt < item.updatedAt) {
      await applyChange(item)
      restored += 1
    }
  }

  // Ненулевое значение — повод посмотреть, почему теряются вебхуки
  if (restored > 0) reportMetric("integration.reconciled", restored)
}

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

Правило выбора

  1. Задержка в минуты допустима, событий мало — опрос, он дешевле и надёжнее
  2. Нужна реакция в секунды — вебхуки с подписью, очередью и защитой от повторов
  3. Данные критичны для денег или обязательств — вебхуки плюс регулярная сверка
  4. Источник не умеет вебхуки — опрос с постраничной выборкой по метке изменения

Частые вопросы

Как часто можно опрашивать чужое API
Смотрите на лимиты в документации, обычно это несколько запросов в секунду. Практически для большинства бизнес-задач достаточно интервала в 5–15 минут при выборке только изменившихся записей — так вы не приближаетесь к лимитам вообще.
Что делать, если вебхуки приходят, а обработка падает
Никогда не возвращать источнику ошибку после того, как событие сохранено. Правильная схема: принять, записать, ответить успехом, обрабатывать из очереди с повторами. Тогда сбой обработки не приводит к потере события.
Нужен ли отдельный сервис для интеграций
При двух-трёх интеграциях достаточно модуля в приложении. Отдельный сервис оправдан, когда систем больше пяти или когда обмен нужно масштабировать независимо от сайта.
интеграцииAPIвебхукиархитектура

Похожая задача в вашем проекте?

Разберём вашу ситуацию и пришлём оценку по этапам — без обязательств и общих слов.

Обсудить задачу

Вопрос по статье
или по своему проекту

Отвечаем на заявки в течение одного рабочего дня. Если задача не наша — скажем прямо и подскажем, к кому обратиться.

Написать нам

Поля со звёздочкой обязательны для заполнения.