メインコンテンツへスキップ
8分で読めます

Real-Time Data API

Access live visitor counts and real-time analytics data through public JSON endpoints. These endpoints are designed for displaying live statistics on your website and require no authentication.

Overview

Real-time endpoints provide:

  • Instant access - No authentication required
  • Live updates - Data refreshes every 5-30 seconds
  • Low latency - Served from Cloudflare's global edge network
  • CORS enabled - Safe to call directly from browsers

Base URL

All real-time endpoints are available at:

https://api.zenovay.com/e

Live Visitor Count

Get the current number of visitors on your website:

GET/e/live/:trackingCode

Get live visitor count

RequestBash
curl -X GET 'https://api.zenovay.com/e/live/ZV_XXXXXXXXXXX'
Response (200 OK)JSON
{
"liveCount": 42,
"timestamp": "2025-01-20T14:30:00Z"
}

Response Fields:

FieldTypeDescription
liveCountnumberCurrent visitors online (active in last 5 minutes)
timestampstringISO 8601 timestamp of the data

Real-Time Analytics

Get comprehensive real-time analytics including visitors, pageviews, and geographic data:

GET/e/realtime/:websiteId

Get real-time analytics data

RequestBash
curl -X GET 'https://api.zenovay.com/e/realtime/ws_abc123'
Response (200 OK)JSON
{
"visitors": {
  "current": 42,
  "today": 1234,
  "change": 12.5
},
"pageViews": {
  "current": 156,
  "today": 4567,
  "change": 8.3
},
"countries": [
  { "code": "US", "name": "United States", "count": 18 },
  { "code": "GB", "name": "United Kingdom", "count": 8 },
  { "code": "DE", "name": "Germany", "count": 6 }
],
"topPages": [
  { "path": "/", "visitors": 15 },
  { "path": "/pricing", "visitors": 8 },
  { "path": "/features", "visitors": 5 }
],
"timestamp": "2025-01-20T14:30:00Z"
}

Response Fields:

FieldTypeDescription
visitors.currentnumberVisitors online now
visitors.todaynumberTotal unique visitors today
visitors.changenumberPercentage change vs yesterday
pageViews.currentnumberPageviews in last 5 minutes
pageViews.todaynumberTotal pageviews today
countriesarrayTop countries by current visitors
topPagesarrayTop pages by current visitors

Visitor Statistics

Get detailed visitor statistics including bounce rate and session duration:

GET/e/stats/:trackingCode

Get visitor statistics

RequestBash
curl -X GET 'https://api.zenovay.com/e/stats/ZV_XXXXXXXXXXX'
Response (200 OK)JSON
{
"visitors": {
  "live": 42,
  "today": 1234,
  "week": 8765,
  "month": 34521
},
"bounceRate": 0.42,
"avgSessionDuration": 185,
"pagesPerSession": 2.9,
"newVisitorRate": 0.65,
"returningVisitorRate": 0.35,
"timestamp": "2025-01-20T14:30:00Z"
}

Response Fields:

FieldTypeDescription
visitors.livenumberCurrent visitors online
visitors.todaynumberUnique visitors today
visitors.weeknumberUnique visitors this week
visitors.monthnumberUnique visitors this month
bounceRatenumberBounce rate (0-1)
avgSessionDurationnumberAverage session length in seconds
pagesPerSessionnumberAverage pages per session
newVisitorRatenumberPercentage of new visitors
returningVisitorRatenumberPercentage of returning visitors

Website Status

Check if tracking is active for a website:

GET/e/:trackingCode/status

Check tracking status

RequestBash
curl -X GET 'https://api.zenovay.com/e/ZV_XXXXXXXXXXX/status'
Response (200 OK)JSON
{
"active": true,
"tracking_code": "ZV_XXXXXXXXXXX",
"domain": "example.com",
"last_event": "2025-01-20T14:29:55Z"
}

JavaScript Integration

Basic Live Counter

Display live visitor count on your website:

Live Visitor CounterJavaScript
class LiveVisitorCounter {
constructor(trackingCode, elementId) {
  this.trackingCode = trackingCode;
  this.element = document.getElementById(elementId);
  this.intervalId = null;
}

async fetchCount() {
  try {
    const response = await fetch(
      `https://api.zenovay.com/e/live/${this.trackingCode}`
    );
    const data = await response.json();
    this.updateDisplay(data.liveCount);
  } catch (error) {
    console.error('Failed to fetch visitor count:', error);
  }
}

updateDisplay(count) {
  if (this.element) {
    this.element.textContent = count.toLocaleString();
    this.element.classList.add('updated');
    setTimeout(() => this.element.classList.remove('updated'), 300);
  }
}

start(intervalMs = 30000) {
  this.fetchCount();
  this.intervalId = setInterval(() => this.fetchCount(), intervalMs);
}

stop() {
  if (this.intervalId) {
    clearInterval(this.intervalId);
  }
}
}

// Usage
const counter = new LiveVisitorCounter('ZV_XXXXXXXXXXX', 'visitor-count');
counter.start();

Real-Time Dashboard

Build a mini analytics dashboard:

Real-Time DashboardJavaScript
async function updateDashboard(websiteId) {
try {
  const response = await fetch(
    `https://api.zenovay.com/e/realtime/${websiteId}`
  );
  const data = await response.json();

  // Update visitor counts
  document.getElementById('live-visitors').textContent = data.visitors.current;
  document.getElementById('today-visitors').textContent = data.visitors.today.toLocaleString();

  // Update pageviews
  document.getElementById('live-pageviews').textContent = data.pageViews.current;

  // Update top countries
  const countriesList = document.getElementById('top-countries');
  countriesList.innerHTML = data.countries
    .slice(0, 5)
    .map(c => `<li>${c.name}: ${c.count}</li>`)
    .join('');

  // Update top pages
  const pagesList = document.getElementById('top-pages');
  pagesList.innerHTML = data.topPages
    .slice(0, 5)
    .map(p => `<li>${p.path}: ${p.visitors}</li>`)
    .join('');

} catch (error) {
  console.error('Dashboard update failed:', error);
}
}

// Update every 30 seconds
setInterval(() => updateDashboard('ws_abc123'), 30000);
updateDashboard('ws_abc123');

React Hook

Custom React hook for real-time data:

useRealtimeAnalytics HookTSX
import { useState, useEffect, useCallback } from 'react';

interface RealtimeData {
visitors: {
  current: number;
  today: number;
  change: number;
};
pageViews: {
  current: number;
  today: number;
  change: number;
};
countries: Array<{ code: string; name: string; count: number }>;
topPages: Array<{ path: string; visitors: number }>;
timestamp: string;
}

function useRealtimeAnalytics(websiteId: string, refreshInterval = 30000) {
const [data, setData] = useState<RealtimeData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);

const fetchData = useCallback(async () => {
  try {
    const response = await fetch(
      `https://api.zenovay.com/e/realtime/${websiteId}`
    );
    if (!response.ok) throw new Error('Failed to fetch');
    const json = await response.json();
    setData(json);
    setError(null);
  } catch (err) {
    setError(err instanceof Error ? err : new Error('Unknown error'));
  } finally {
    setLoading(false);
  }
}, [websiteId]);

useEffect(() => {
  fetchData();
  const interval = setInterval(fetchData, refreshInterval);
  return () => clearInterval(interval);
}, [fetchData, refreshInterval]);

return { data, loading, error, refetch: fetchData };
}

// Usage in component
function AnalyticsDashboard({ websiteId }: { websiteId: string }) {
const { data, loading, error } = useRealtimeAnalytics(websiteId);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return null;

return (
  <div className="dashboard">
    <div className="stat">
      <span className="label">Live Visitors</span>
      <span className="value">{data.visitors.current}</span>
    </div>
    <div className="stat">
      <span className="label">Today</span>
      <span className="value">{data.visitors.today.toLocaleString()}</span>
    </div>
  </div>
);
}

Vue Composable

Vue 3 composable for real-time data:

useRealtimeAnalytics ComposableTypeScript
import { ref, onMounted, onUnmounted, watch } from 'vue';

interface RealtimeData {
visitors: { current: number; today: number; change: number };
pageViews: { current: number; today: number; change: number };
countries: Array<{ code: string; name: string; count: number }>;
topPages: Array<{ path: string; visitors: number }>;
}

export function useRealtimeAnalytics(websiteId: string, refreshInterval = 30000) {
const data = ref<RealtimeData | null>(null);
const loading = ref(true);
const error = ref<Error | null>(null);
let intervalId: number | null = null;

async function fetchData() {
  try {
    const response = await fetch(
      `https://api.zenovay.com/e/realtime/${websiteId}`
    );
    if (!response.ok) throw new Error('Failed to fetch');
    data.value = await response.json();
    error.value = null;
  } catch (err) {
    error.value = err instanceof Error ? err : new Error('Unknown error');
  } finally {
    loading.value = false;
  }
}

onMounted(() => {
  fetchData();
  intervalId = window.setInterval(fetchData, refreshInterval);
});

onUnmounted(() => {
  if (intervalId) clearInterval(intervalId);
});

return { data, loading, error, refetch: fetchData };
}

Rate Limits

Real-time endpoints have generous rate limits:

EndpointRate LimitCache TTL
/e/live/:trackingCode1000 req/min5 seconds
/e/realtime/:websiteId500 req/min10 seconds
/e/stats/:trackingCode500 req/min30 seconds
/e/:trackingCode/status100 req/min60 seconds

Data is cached at the edge. Multiple requests within the cache TTL will receive the same data, making it safe to poll frequently from client-side code.

CORS Configuration

All real-time endpoints support CORS and can be called directly from browsers:

CORS HeadersTEXT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type

Error Handling

Error Handling ExampleJavaScript
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
  try {
    const response = await fetch(url);

    if (response.status === 429) {
      // Rate limited - wait and retry
      const retryAfter = response.headers.get('Retry-After') || '60';
      await new Promise(r => setTimeout(r, parseInt(retryAfter) * 1000));
      continue;
    }

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    if (i === maxRetries - 1) throw error;
    // Exponential backoff
    await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
  }
}
}

// Usage
try {
const data = await fetchWithRetry('https://api.zenovay.com/e/live/ZV_XXXXXXXXXXX');
console.log('Live visitors:', data.liveCount);
} catch (error) {
console.error('Failed after retries:', error);
}

Best Practices

  1. Cache locally - Store data in memory to avoid unnecessary requests during rapid UI updates
  2. Debounce updates - Don't trigger UI updates faster than the cache TTL
  3. Handle offline - Show stale data with a timestamp when the network is unavailable
  4. Animate changes - Smooth transitions when counts change make the UI feel more responsive
  5. Show loading states - Display skeleton loaders during initial fetch

Next Steps

このページは役に立ちましたか?