Free25 minutesadvanced

Server-Side Tracking

Track events and pageviews server-side using the Zenovay tracking endpoint for enhanced accuracy and privacy compliance.

server-sideapitrackingbackendprivacy
Last updated: January 15, 2025

Track analytics events from your server for enhanced accuracy, ad-blocker immunity, and complete control over data collection.

Why Server-Side Tracking?

Benefits

BenefitDescription
Ad-blocker immunityEvents tracked regardless of client blockers
Enhanced accuracyNo JavaScript required, works everywhere
Bot filteringFilter bots before tracking
Data controlFull control over what data is sent
Privacy complianceEasier GDPR/CCPA compliance
Sensitive eventsTrack backend events (purchases, signups)

When to Use

  • E-commerce purchase tracking
  • Form submissions
  • Backend events (subscriptions, cancellations)
  • Hybrid tracking (client + server)
  • Privacy-sensitive environments

Tracking Endpoint

Server-side tracking uses the same tracking endpoint as the client-side script. Send a POST request to the tracking endpoint with your website's tracking code:

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

This is a public endpoint and does not require an API key. The tracking code is the same code used in the data-tracking-code attribute of your tracking script.

Request Format

curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "event",
    "url": "https://example.com/checkout/success",
    "referrer": "https://example.com/cart",
    "name": "purchase",
    "data": {
      "order_id": "ORD-12345",
      "value": 99.99,
      "currency": "USD"
    }
  }'

Passing Visitor Information

For accurate geolocation and device detection, forward the original visitor's headers:

curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -H "User-Agent: Mozilla/5.0..." \
  -H "X-Forwarded-For: 203.0.113.50" \
  -d '{
    "type": "pageview",
    "url": "https://example.com/products/widget",
    "referrer": "https://google.com/search"
  }'

Implementation Examples

Node.js / Express

// lib/analytics.js
const TRACKING_CODE = process.env.ZENOVAY_TRACKING_CODE;

async function trackEvent(name, data, req = null) {
  const payload = {
    type: 'event',
    url: data.url || req?.headers?.referer,
    name: name,
    data: data.properties || {}
  };

  const headers = {
    'Content-Type': 'application/json'
  };

  // Forward visitor headers for geolocation
  if (req) {
    headers['User-Agent'] = req.headers['user-agent'];
    headers['X-Forwarded-For'] = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
  }

  try {
    const response = await fetch(
      `https://api.zenovay.com/e/${TRACKING_CODE}`,
      {
        method: 'POST',
        headers,
        body: JSON.stringify(payload)
      }
    );

    return response.ok;
  } catch (error) {
    console.error('Analytics error:', error);
    return false;
  }
}

async function trackPageview(url, req) {
  const payload = {
    type: 'pageview',
    url
  };

  const headers = {
    'Content-Type': 'application/json'
  };

  if (req) {
    headers['User-Agent'] = req.headers['user-agent'];
    headers['X-Forwarded-For'] = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
  }

  try {
    const response = await fetch(
      `https://api.zenovay.com/e/${TRACKING_CODE}`,
      {
        method: 'POST',
        headers,
        body: JSON.stringify(payload)
      }
    );

    return response.ok;
  } catch (error) {
    console.error('Pageview tracking error:', error);
    return false;
  }
}

module.exports = { trackEvent, trackPageview };

Express.js Middleware

// middleware/analytics.js
const { trackPageview } = require('../lib/analytics');

function analyticsMiddleware(options = {}) {
  return async (req, res, next) => {
    // Skip static files and API routes
    if (req.path.match(/\.(js|css|png|jpg|ico)$/) || req.path.startsWith('/api/')) {
      return next();
    }

    // Track pageview asynchronously (don't block response)
    trackPageview(req.originalUrl, req).catch(console.error);

    next();
  };
}

module.exports = analyticsMiddleware;

// app.js
const analyticsMiddleware = require('./middleware/analytics');
app.use(analyticsMiddleware());

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}'

def track_event(event_name, properties=None, url=None):
    payload = {
        'type': 'event',
        'url': url or request.url,
        'name': event_name,
        'data': properties or {}
    }

    headers = {
        'Content-Type': 'application/json',
        'User-Agent': request.headers.get('User-Agent', ''),
        'X-Forwarded-For': request.headers.get('X-Forwarded-For', request.remote_addr)
    }

    try:
        response = requests.post(TRACKING_URL, json=payload, headers=headers, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Analytics error: {e}')
        return False

def track_pageview(url=None):
    payload = {
        'type': 'pageview',
        'url': url or request.url,
        'referrer': request.headers.get('Referer', '')
    }

    headers = {
        'Content-Type': 'application/json',
        'User-Agent': request.headers.get('User-Agent', ''),
        'X-Forwarded-For': request.headers.get('X-Forwarded-For', request.remote_addr)
    }

    try:
        response = requests.post(TRACKING_URL, json=payload, headers=headers, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Pageview tracking error: {e}')
        return False

PHP

<?php
class ZenovayAnalytics {
    private $trackingCode;
    private $trackingUrl;

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

    public function trackEvent($eventName, $properties = [], $url = null) {
        $payload = [
            'type' => 'event',
            'url' => $url ?? 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
            'name' => $eventName,
            'data' => $properties
        ];
        return $this->sendRequest($payload);
    }

    public function trackPageview($url = null) {
        $payload = [
            'type' => 'pageview',
            'url' => $url ?? 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
            'referrer' => $_SERVER['HTTP_REFERER'] ?? ''
        ];
        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',
                'User-Agent: ' . ($_SERVER['HTTP_USER_AGENT'] ?? ''),
                'X-Forwarded-For: ' . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '')
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5
        ]);

        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $httpCode >= 200 && $httpCode < 300;
    }
}

// Usage
$analytics = new ZenovayAnalytics(getenv('ZENOVAY_TRACKING_CODE'));
$analytics->trackEvent('purchase', ['order_id' => 'ORD-12345', 'value' => 99.99]);

Hybrid Tracking

Combine client and server-side tracking for the best of both worlds:

// Client-side: Track interactive events using the Zenovay script
window.zenovay('track', 'checkout_started');

// Server-side: Track confirmed backend events
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',
      properties: {
        order_id: event.data.object.id,
        value: event.data.object.amount_total / 100
      }
    });
  }

  res.sendStatus(200);
});

Bot Filtering

Filter bots before tracking:

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));
}

app.use((req, res, next) => {
  if (!isBot(req.headers['user-agent'])) {
    trackPageview(req.originalUrl, req);
  }
  next();
});

Rate Limits

The tracking endpoint has rate limits applied per IP address:

  • Burst limit: 60 requests per 10 seconds
  • Sustained limit: 5,000 requests per hour

These limits apply to the tracking endpoint specifically, not the External API.

Troubleshooting

Events Not Appearing

Check:

  • Tracking code is correct
  • Request format matches specification
  • IP not blocked or rate limited
  • Correct Content-Type header

Duplicate Events

Ensure:

  • Not tracking both client and server for the same event
  • Check for middleware running multiple times

Missing Geolocation

Verify:

  • X-Forwarded-For header is being passed with the visitor's real IP
  • Not using localhost/private IPs in production

Next Steps

Was this article helpful?