10 min di letturaIntegrazioni

Webhook vs polling: webhook per il tracciamento dei click

Webhook vs polling per il tracciamento dei click: calcoli su ritardo e richieste, retry, idempotenza e firme, con tabella decisionale e codice Node eseguito.

Sasha Ehrlich
Compliance · EU residency

Usa i webhook quando una persona o un workflow deve reagire in pochi secondi, e fai polling quando qualche minuto di ritardo va bene e preferisci non gestire un endpoint pubblico. Su Elido la scelta ha un'avvertenza: i webhook per il tracciamento dei click non esistono ancora. I webhook coprono eventi di link, domini, membri e workspace, e un evento click.created per singolo click è previsto ma non emesso. Oggi i dati dei click arrivano dal polling dell'API analytics.

Questo articolo fa i conti su ritardo e volume di richieste, copre cosa garantisce ciascun modello (retry, duplicati, ordinamento, firme) e finisce con una tabella decisionale. Il codice è stato eseguito in Node 22 contro mock locali prima di essere inserito. Per il catalogo completo degli eventi dei link, leggi webhook per gli eventi dei link; per gli endpoint dei click, la guida all'API analytics dei link.

Webhook vs polling in un paragrafo ciascuno

Polling significa che il tuo codice chiede nuovi dati a intervalli. Un cron job chiama GET /v1/workspaces/{workspace_id}/analytics/clicks/recent, gestisce ciò che è nuovo e va a dormire. Sei tu a iniziare ogni richiesta. Non serve un URL pubblico.

I webhook invertono la direzione. Elido invia una POST firmata con un envelope JSON al tuo endpoint HTTPS quando scatta un evento, e una risposta 2xx segna la consegna come completata. Ti serve un endpoint raggiungibile. TLS. Un handler che regga i duplicati. Oggi gli eventi sono link.created, link.updated, link.expired, domain.verified e il resto dell'elenco nella documentazione dei webhook.

Diagramma affiancato: nel polling il tuo cron job invia GET all'API di Elido e preleva righe ogni cinque minuti; nei webhook il server Elido invia una POST al tuo endpoint HTTPS che risponde 2xx, con l'evento click.created per singolo click ancora previsto. La pagina Webhook di Elido con un endpoint, i suoi eventi dei link sottoscritti, uno stato attivo e la data di creazione

L'aritmetica di latenza e costo

Il ritardo del polling è fissato dall'intervallo. Con un intervallo di I minuti, un evento aspetta in media I/2 minuti e nel caso peggiore I minuti, più l'eventuale ritardo che i dati hanno prima di poter essere interrogati. Le richieste al giorno sono 1.440 / I.

  • 1 minuto: 1.440 richieste al giorno, 30 secondi di ritardo medio, 1 minuto nel caso peggiore.
  • 5 minuti: 288 richieste, 2,5 minuti in media, 5 minuti nel caso peggiore.
  • 15 minuti: 96 richieste, 7,5 minuti in media, 15 minuti nel caso peggiore.
  • 60 minuti: 24 richieste, 30 minuti in media, 60 minuti nel caso peggiore.

Il costo si vede come richieste sprecate, non come pressione sui rate limit. Il limite pubblicato per workspace sul piano Free è di 10 richieste al secondo, cioè 864.000 al giorno (vedi rate limit e idempotenza delle API). Un solo poller con intervallo di 1 minuto usa 1.440 / 864.000 = 0,17% di quel limite. Raggiungerai i limiti facendo polling per singolo link su centinaia di link, o quando altre automazioni condividono il workspace, non con un solo feed.

Lo spreco è il numero che conta. Diciamo che un workspace modifica 40 link al giorno e li sorvegli con un polling ogni minuto. Al massimo 40 delle 1.440 richieste restituiscono qualcosa di nuovo, quindi almeno 1.400 (97%) sono vuote. Un ricevente webhook riceve 40 richieste per gli stessi 40 eventi. Nel caso peggiore, con tre tentativi ciascuno, sono 120.

I webhook vincono sul ritardo, che di solito è di secondi perché il worker di consegna preleva continuamente le righe in sospeso. Perdono su ciò che il polling ottiene gratis: un'esecuzione saltata viene riparata dall'esecuzione successiva, perché i dati sono ancora nell'API. Un webhook perso viene riparato solo se hai costruito qualcosa per ripararlo.

Garanzie di consegna: cosa promette Elido

Questi comportamenti vengono dal codice di consegna e dalla documentazione di aiuto, verificati il 1 ottobre 2026.

Retry. Una consegna ha tre tentativi: uno subito, uno 5 minuti dopo un errore, uno 15 minuti dopo ancora, circa 20 minuti da un capo all'altro. Qualsiasi status non 2xx, errore di rete o risposta più lenta di 10 secondi è un fallimento. Dopo il terzo, la consegna viene segnata come fallita; Retry nella dashboard o POST .../deliveries/{delivery_id}/retry la riarma con un nuovo budget di tre tentativi. Per confronto, Stripe ritenta gli eventi in live mode fino a tre giorni. Venti minuti sono pochi. Non dare per scontato che un'interruzione del ricevente venga coperta.

Idempotenza. La consegna è at least once. L'header X-Webhook-Delivery identifica una consegna verso un endpoint e non cambia tra retry automatici o manuali, quindi è la tua chiave di deduplicazione.

Ordinamento. Non c'è alcuna promessa di ordinamento. Un retry di un evento precedente può arrivare dopo uno successivo, quindi confronta data.updated_at con ciò che hai salvato prima di sovrascrivere qualsiasi cosa.

Firme. Ogni consegna firmata porta X-Webhook-Signature (alias X-Elido-Signature) con v1= più l'HMAC-SHA256 esadecimale di {timestamp}.{raw_body}, e il timestamp in X-Webhook-Timestamp. Il timestamp è nuovo a ogni tentativo, quindi un retry legittimo non è mai scaduto. Durante una rotazione del segreto, X-Elido-Signature-Previous porta per sette giorni una firma con il vecchio segreto. Verificare le firme dei webhook copre Python, Go e il debug di una discrepanza.

Trasporto e pausa. I nuovi endpoint di eventi devono usare https; http semplice è accettato solo per localhost e 127.0.0.1. Impostare is_active a false mette in pausa un endpoint. Dopo la correzione del 22 settembre, questo ferma anche le consegne in coda.

Un ricevente webhook in Node

Questo ricevente verifica la firma sui byte grezzi, impone una finestra di cinque minuti, accetta l'header del segreto precedente durante la rotazione e deduplica sull'ID della consegna. La tolleranza di cinque minuti è una tua scelta, non di Elido, e coincide con il valore predefinito di Stripe. L'ho eseguito contro un firmatario locale: una richiesta valida ha restituito 200, un replay con lo stesso ID di consegna ha restituito "duplicate", un segreto sbagliato e un timestamp vecchio di 400 secondi hanno restituito entrambi 401.

import http from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";

const SECRET = process.env.WEBHOOK_SECRET; // the whsec_... value
const seen = new Set(); // use a unique index in a real database

export function verify(secret, headers, rawBody, toleranceSec = 300) {
  const ts = headers["x-webhook-timestamp"] ?? "";
  if (!/^\d+$/.test(ts)) return false;
  if (Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSec) return false;
  const mac = createHmac("sha256", secret).update(`${ts}.`).update(rawBody);
  const expected = Buffer.from("v1=" + mac.digest("hex"));
  return ["x-elido-signature", "x-elido-signature-previous"].some((name) => {
    const got = Buffer.from(headers[name] ?? "");
    return got.length === expected.length && timingSafeEqual(got, expected);
  });
}

http
  .createServer((req, res) => {
    const chunks = [];
    req.on("data", (c) => chunks.push(c));
    req.on("end", () => {
      const raw = Buffer.concat(chunks);
      if (req.method !== "POST" || !verify(SECRET, req.headers, raw)) {
        return res.writeHead(401).end();
      }
      const id = req.headers["x-webhook-delivery"];
      if (seen.has(id)) return res.writeHead(200).end("duplicate");
      seen.add(id);
      const event = JSON.parse(raw.toString("utf8"));
      queueMicrotask(() => console.log("handled", event.type)); // do the work after replying
      res.writeHead(200).end("ok");
    });
  })
  .listen(8787);

Il controllo di lunghezza prima di timingSafeEqual è importante. Node lancia un errore con buffer di dimensioni diverse. Rispondi prima, lavora dopo. Il limite di 10 secondi è il punto in cui un handler lento ma riuscito diventa un duplicato.

Un poller di click in Node

Finché click.created non arriva, questo è il percorso dei click. clicks/recent restituisce prima i più recenti, accetta limit fino a 500 (predefinito 100) e restituisce next_cursor ogni volta che una pagina torna piena. Ogni esecuzione legge dall'alto e si ferma quando raggiunge il click più recente già gestito. L'ho eseguito contro un mock che pagina due righe alla volta: la prima esecuzione ha gestito 5 click, la seconda solo i 2 nuovi, la terza nessuno.

const BASE = "https://api.elido.app";
const WS = process.env.ELIDO_WORKSPACE_ID;
const KEY = process.env.ELIDO_API_KEY;

// Newest click already handled; clicks/recent is newest first.
let lastSeen = null;
const keyOf = (c) => `${c.ts}|${c.link_id}`;

export async function pollOnce(handle) {
  let cursor = null;
  let newest = null;
  for (;;) {
    const url = new URL(`${BASE}/v1/workspaces/${WS}/analytics/clicks/recent`);
    url.searchParams.set("limit", "500");
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${KEY}` },
      signal: AbortSignal.timeout(10_000),
    });
    if (!res.ok) throw new Error(`clicks/recent returned ${res.status}`);
    const { items, next_cursor } = await res.json();
    for (const click of items) {
      if (keyOf(click) === lastSeen) {
        if (newest) lastSeen = newest;
        return;
      }
      newest ??= keyOf(click);
      await handle(click);
    }
    if (!next_cursor) break;
    cursor = next_cursor;
  }
  if (newest) lastSeen = newest;
}

// setInterval(() => pollOnce(handleClick).catch(console.error), 60_000);

Due limiti, detti chiaramente. Il cursore ordina per timestamp e ID del link, quindi due click sullo stesso link nello stesso secondo condividono una chiave; se non puoi permetterti un click raramente saltato o ripetuto, rendi handle idempotente sull'intera riga. E lastSeen vive in memoria, quindi persistilo tra i riavvii, altrimenti un riavvio rigioca la storia recente.

Non sei pronto a costruire nessuno dei due? Va bene. Apri la dashboard e usa prima le analytics della dashboard, poi aggiungi l'API quando sai quali numeri ti servono a cadenza fissa.

Tabella decisionale

SituazioneCosa usare oggiPerché
Reagire a modifiche di link o domini in secondiWebhook su link.*, domain.*Gli eventi sono inviati; non serve un evento di click
Contatore di click in tempo reale nel tuo prodottoPolling a 1 minuto di clicks/recentAncora nessun webhook di click; 1.440 richieste al giorno sono ben dentro il limite
Arricchimento CRM per singolo clickEvent forwarder o polling breveUn ritardo medio di 30 secondi di solito è accettabile
Report settimanali o mensiliPolling di summary e timeseriesI dati retrospettivi non guadagnano nulla dal push
Nessun endpoint pubblico disponibilePollingI webhook richiedono un URL https raggiungibile da internet
Non puoi perdere un evento di linkWebhook più riconciliazione notturnaI retry coprono circa 20 minuti, il polling copre il resto

Quando arriverà lo stream click.created campionato, le righe del contatore live e del CRM passeranno alla colonna dei webhook, e il polling diventerà il passaggio di riconciliazione. Fino ad allora, gli event forwarder come gli eventi di click dei link su Mixpanel sono l'opzione in stile push per i dati dei click.

Il modello ibrido

Per tutto ciò che non puoi permetterti di perdere, usa entrambi. Il webhook è la via veloce. Un polling pianificato confronta i totali e trova i buchi: consegne che hanno esaurito i tre tentativi durante un'interruzione, o eventi arrivati fuori ordine.

I report summary e timeseries restituiscono totali e bucket giornalieri per un intervallo di date in una sola chiamata, il che rende economico un confronto notturno. Ricorda che il filtro dei bot rimuove crawler e monitor prima che i click vengano registrati, quindi i tuoi totali non coincideranno con i log grezzi del server; la guida alle analytics spiega cosa viene escluso. La checklist di sicurezza copre il resto. Se progetti i tuoi webhook, la specifica Standard Webhooks è un utile riferimento.

Pagina della documentazione API nella dashboard di Elido con link al riferimento API, agli SDK, alla specifica OpenAPI e alla gestione delle chiavi API

La schermata della documentazione API di un workspace DEMO con dati di esempio; non è un account cliente.

Una checklist pre-lancio per entrambi i modelli

Prima di rilasciare un ricevente o un poller, fai sei controlli. La maggior parte degli incidenti in produzione con entrambi i modelli nasce dall'aver saltato uno di essi.

  • Webhook: rispondi con 2xx entro 10 secondi e fai il lavoro lento dopo aver risposto.
  • Webhook: verifica la firma sul corpo grezzo prima di fare il parsing del JSON.
  • Webhook: salva X-Webhook-Delivery con un indice unico, così un retry non può contare due volte.
  • Poller: persisti il cursore o l'ultima chiave vista fuori dalla memoria del processo.
  • Poller: fai backoff su un 429 o 5xx invece di ritentare all'intervallo normale.
  • Entrambi: allerta quando non è arrivato alcun evento o nuovo click per più del tuo normale periodo di quiete, perché il silenzio è l'aspetto di un'integrazione rotta. Articoli indipendenti come la guida di Hookdeck su webhook vs API polling arrivano allo stesso consiglio: push per la velocità, polling per riconciliare.

Correlati sul blog

Domande frequenti

Qual è la differenza tra webhook e polling?

Con il polling il tuo codice chiede all'API nuovi dati a intervalli. Con i webhook il server invia una POST HTTP al tuo endpoint quando succede qualcosa. Il polling costa richieste e aggiunge fino a un intervallo di ritardo. I webhook costano un endpoint pubblico sempre attivo e il lavoro di gestire retry e duplicati.

Elido ha webhook per il tracciamento dei click?

Non ancora. I webhook coprono eventi di link, domini, membri e workspace come link.created e link.expired. Un evento click.created è previsto ma oggi nulla lo pubblica. Per i dati a livello di click, fai polling del report clicks/recent dell'API analytics con il suo cursore, oppure usa un event forwarder.

Ogni quanto devo fare polling di un'API analytics?

Adatta l'intervallo alla decisione che i dati alimentano. Un minuto va bene per un contatore quasi in tempo reale, da cinque a quindici minuti per le dashboard, un'ora o un giorno per i report. Un polling a 1 minuto fa 1.440 richieste al giorno e uno a 5 minuti ne fa 288, quindi andare più corto raramente vale la pena.

I webhook arrivano garantiti esattamente una volta?

No. La consegna dei webhook è at least once, quindi un ricevente vede duplicati dopo un timeout o un reinvio manuale. Elido mantiene lo stesso valore X-Webhook-Delivery tra i retry, quindi salva quell'ID e salta ciò che hai già gestito.

Cosa succede se il mio endpoint webhook è giù?

Elido fa tre tentativi: uno subito, uno cinque minuti dopo un errore e uno quindici minuti dopo ancora. Se falliscono tutti e tre, la consegna viene segnata come fallita finché qualcuno preme Retry o chiama l'endpoint di retry. Le interruzioni più lunghe di circa venti minuti richiedono un job di riconciliazione.

Posso combinare webhook e polling?

Sì, e per tutto ciò che conta dovresti. Usa il webhook come via veloce e un polling pianificato come rete di sicurezza che trova gli eventi persi dal tuo ricevente. Il polling può girare ogni ora o ogni notte perché deve solo chiudere i buchi.

Prova Elido

Incolla un URL, ottieni un link breve

Senza registrazione. Il link vive 30 giorni. Iscriviti per conservarlo.

Senza registrazione. Crea un account gratuito per 50 link al mese.

Prova Elido

Accorciatore di URL ospitato nell'UE: domini personalizzati, analisi approfondite e API aperta. Piano gratuito - senza carta di credito.

Tag
webhooks vs polling
click tracking webhooks
click tracking api
url shortener webhooks
webhook retries idempotency
link analytics api

Continua a leggere