Ir al contenido principal
Zenovay
Gratis25 minutesAvanzado

Seguimiento del lado del servidor

Envíe eventos y vistas de página directamente al endpoint de seguimiento de Zenovay desde su servidor para inmunidad contra bloqueadores de anuncios y control total de la recopilación.

server-sideapitrackingbackendprivacy
Última actualización:

Envíe eventos de análisis directamente desde su backend a Zenovay. El seguimiento del lado del servidor es inmune a los bloqueadores de anuncios, le permite registrar eventos que solo ocurren en el servidor (compras confirmadas, suscripciones impulsadas por webhook) y le da control total sobre lo que se envía.

¿Por qué el seguimiento del lado del servidor?

Beneficios

BeneficioDescripción
Inmunidad contra bloqueadores de anunciosLos eventos llegan a Zenovay incluso cuando el script del cliente está bloqueado por el navegador
Eventos solo del servidorRastree cosas que ocurren solo del lado del servidor — pagos confirmados, trabajos cron, webhooks
Control de datosUsted decide exactamente qué se envía
Seguimiento híbridoCombine con el script del cliente para una cobertura completa

Cuándo usar

  • Confirmación de compra en comercio electrónico (después de la confirmación del proveedor de pago)
  • Eventos de backend (suscripción creada, suscripción cancelada)
  • Eventos impulsados por webhook (Stripe, proveedores de pago)
  • Seguimiento híbrido (script del cliente para navegación + servidor para conversiones confirmadas)

Endpoint de seguimiento

Las solicitudes del lado del servidor utilizan el mismo endpoint de ingesta que el script del cliente. Envíe un POST al endpoint de seguimiento con el código de seguimiento de su sitio en la ruta:

POST https://api.zenovay.com/e/{trackingCode}

Este es un endpoint público, no autenticado — no requiere una clave API y está abierto en todos los planes (es la misma ruta a la que el script de seguimiento envía). El código de seguimiento es el mismo valor que pone en el atributo data-tracking-code de su snippet de instalación.

Información

El endpoint de ingesta se comparte con el rastreador del navegador, por lo que su carga útil está estructurada como los datos que enviaría un navegador. Esto significa que algunos campos que el navegador completa automáticamente — session_id, device_type, browser, os y user_agent — deben ser suministrados por usted en el cuerpo JSON cuando se llama desde un servidor. Las solicitudes a las que les faltan estos campos se rechazan.

Campos requeridos

Cada evento que envíe debe incluir estos campos en el cuerpo JSON:

CampoNotas
session_idUna cadena de al menos 8 caracteres. Reutilice el mismo valor para eventos que pertenecen a la misma visita.
urlUna URL completa y válida (por ejemplo https://example.com/checkout/success).
device_typepor ejemplo desktop, mobile, tablet o server.
browserUn nombre de navegador/cliente. Use algo descriptivo para tráfico de servidor (por ejemplo server).
osUn nombre de sistema operativo (por ejemplo Linux).
user_agentUna cadena de agente de usuario. Evite agentes genéricos parecidos a bots (curl, python, wget, etc.) — se filtran como bots.

visitor_id es opcional pero recomendado (≥8 caracteres) para que los eventos repetidos se vinculen al mismo visitante.

Vista de página vs. Evento personalizado

El tipo de evento se establece con el campo event_type:

  • Vista de página — omita event_type (predeterminado) o establézcalo en pageview.
  • Evento personalizado — establezca event_type en custom, coloque el nombre del evento en event_name y cualquier metadato en properties.
# Evento personalizado (por ejemplo, una compra confirmada)
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "custom",
    "event_name": "purchase",
    "url": "https://example.com/checkout/success",
    "referrer": "https://example.com/cart",
    "session_id": "srv-9f2a7c41bd",
    "device_type": "server",
    "browser": "server",
    "os": "Linux",
    "user_agent": "MyApp-Server/1.0",
    "properties": {
      "order_id": "ORD-12345",
      "value": 99.99,
      "currency": "USD"
    }
  }'
# Vista de página
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "pageview",
    "url": "https://example.com/products/widget",
    "referrer": "https://google.com/search",
    "session_id": "srv-9f2a7c41bd",
    "device_type": "server",
    "browser": "server",
    "os": "Linux",
    "user_agent": "MyApp-Server/1.0"
  }'

Una nota sobre la geolocalización

Para el tráfico del navegador, Zenovay obtiene la ubicación de la IP del visitante que se conecta. Cuando llama al endpoint desde su servidor, la IP que se conecta es la de su servidor — por lo que los eventos se geolocalizarán en su infraestructura, no en el usuario final.

Si necesita geolocalización precisa por visitante, realice ese seguimiento del lado del cliente (el snippet de instalación estándar) o use el proxy de primer nivel, y reserve el seguimiento del lado del servidor para eventos de backend (compras, suscripciones) donde la ubicación exacta del usuario no es el punto. Agregar un encabezado X-Forwarded-For en una llamada simple de servidor no anula la IP del servidor para el endpoint público.

Ejemplos de implementación

Node.js / Express

// lib/analytics.js
const TRACKING_CODE = process.env.ZENOVAY_TRACKING_CODE;
const ENDPOINT = `https://api.zenovay.com/e/${TRACKING_CODE}`;

// Campos base que cada evento del lado del servidor necesita.
const SERVER_DEFAULTS = {
  device_type: 'server',
  browser: 'server',
  os: 'Linux',
  user_agent: 'MyApp-Server/1.0'
};

async function trackEvent(eventName, { url, sessionId, properties = {} }) {
  const payload = {
    event_type: 'custom',
    event_name: eventName,
    url,
    session_id: sessionId,
    properties,
    ...SERVER_DEFAULTS
  };

  try {
    const response = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    return response.ok;
  } catch (error) {
    console.error('Analytics error:', error);
    return false;
  }
}

async function trackPageview({ url, sessionId, referrer = '' }) {
  const payload = {
    event_type: 'pageview',
    url,
    referrer,
    session_id: sessionId,
    ...SERVER_DEFAULTS
  };

  try {
    const response = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    return response.ok;
  } catch (error) {
    console.error('Pageview tracking error:', error);
    return false;
  }
}

module.exports = { trackEvent, trackPageview };

Python (Flask)

# analytics.py
import os
import requests
from flask import request

TRACKING_CODE = os.getenv('ZENOVAY_TRACKING_CODE')
TRACKING_URL = f'https://api.zenovay.com/e/{TRACKING_CODE}'

SERVER_DEFAULTS = {
    'device_type': 'server',
    'browser': 'server',
    'os': 'Linux',
    'user_agent': 'MyApp-Server/1.0',
}

def track_event(event_name, session_id, url, properties=None):
    payload = {
        'event_type': 'custom',
        'event_name': event_name,
        'url': url,
        'session_id': session_id,
        'properties': properties or {},
        **SERVER_DEFAULTS,
    }
    try:
        response = requests.post(TRACKING_URL, json=payload, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Analytics error: {e}')
        return False

def track_pageview(session_id, url, referrer=''):
    payload = {
        'event_type': 'pageview',
        'url': url,
        'referrer': referrer,
        'session_id': session_id,
        **SERVER_DEFAULTS,
    }
    try:
        response = requests.post(TRACKING_URL, json=payload, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Pageview tracking error: {e}')
        return False

PHP

<?php
class ZenovayAnalytics {
    private $trackingUrl;
    private $defaults = [
        'device_type' => 'server',
        'browser'     => 'server',
        'os'          => 'Linux',
        'user_agent'  => 'MyApp-Server/1.0',
    ];

    public function __construct($trackingCode) {
        $this->trackingUrl = "https://api.zenovay.com/e/{$trackingCode}";
    }

    public function trackEvent($eventName, $sessionId, $url, $properties = []) {
        $payload = array_merge([
            'event_type' => 'custom',
            'event_name' => $eventName,
            'url'        => $url,
            'session_id' => $sessionId,
            'properties' => $properties,
        ], $this->defaults);
        return $this->sendRequest($payload);
    }

    public function trackPageview($sessionId, $url, $referrer = '') {
        $payload = array_merge([
            'event_type' => 'pageview',
            'url'        => $url,
            'referrer'   => $referrer,
            'session_id' => $sessionId,
        ], $this->defaults);
        return $this->sendRequest($payload);
    }

    private function sendRequest($payload) {
        $ch = curl_init($this->trackingUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5
        ]);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $httpCode >= 200 && $httpCode < 300;
    }
}

// Uso
$analytics = new ZenovayAnalytics(getenv('ZENOVAY_TRACKING_CODE'));
$analytics->trackEvent('purchase', 'srv-9f2a7c41bd', 'https://example.com/checkout/success', [
    'order_id' => 'ORD-12345',
    'value'    => 99.99
]);

Seguimiento híbrido

El patrón más común: rastree la navegación con el script del cliente y confirme conversiones desde el servidor una vez que el proveedor de pago las ha confirmado.

// Del lado del cliente: rastree la intención usando el script de Zenovay
window.zenovay('track', 'checkout_started');

// Del lado del servidor: rastree el evento confirmado desde su controlador de webhook
app.post('/webhooks/stripe', async (req, res) => {
  const event = req.body;

  if (event.type === 'checkout.session.completed') {
    await trackEvent('purchase', {
      url: 'https://example.com/checkout/success',
      sessionId: 'srv-' + event.data.object.id.slice(-12),
      properties: {
        order_id: event.data.object.id,
        value: event.data.object.amount_total / 100
      }
    });
  }

  res.sendStatus(200);
});

Filtrado de bots

Zenovay ya filtra tráfico de bot obvio en el lado de ingesta, por lo que los eventos enviados con agentes genéricos como curl, wget, python o cualquier cosa que coincida con patrones de crawler comunes se rechazan. Por eso los ejemplos anteriores usan un user_agent descriptivo para su servidor.

Si también reenvía solicitudes de cliente reales a través de su backend, filtre los crawlers antes de enviar para no quemar su límite de tasa en tráfico que de todas formas sería rechazado:

function isBot(userAgent) {
  const botPatterns = [
    /bot/i, /crawler/i, /spider/i, /scraper/i,
    /curl/i, /wget/i, /python/i, /java\//i,
    /googlebot/i, /bingbot/i, /yandex/i
  ];
  return botPatterns.some(pattern => pattern.test(userAgent || ''));
}

Límites de velocidad

El endpoint de seguimiento tiene límites de velocidad por dirección IP:

  • Límite de ráfaga: 60 solicitudes por 10 segundos
  • Límite sostenido: 5.000 solicitudes por hora

Estos límites se aplican específicamente al endpoint de ingesta de seguimiento, no a la API REST (que es una característica pagada separada con sus propios límites).

Solución de problemas

Los eventos no aparecen

Verifique:

  • El código de seguimiento en la ruta URL es correcto.
  • Todos los campos requeridos están presentes (session_id de 8+ caracteres, url válida, device_type, browser, os, user_agent). Los campos faltantes devuelven un 400 con una lista de lo que falta.
  • Su user_agent no se está filtrando como un bot (evite curl, python, etc.).
  • La IP no tiene límite de velocidad.

Eventos duplicados

Asegúrese:

  • No está rastreando el mismo evento tanto del lado del cliente como del servidor.
  • Los controladores de webhook no se disparan más de una vez para el mismo evento.

La geolocalización refleja su servidor

Esto es esperado para las llamadas del lado del servidor — la IP que se conecta es su servidor, no el visitante. Use el script del cliente (o un proxy de primer nivel) para geolocalización precisa del visitante y use el seguimiento del lado del servidor para eventos de backend donde la ubicación no es el objetivo.

Próximos pasos

¿Fue útil este artículo?