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

リアルタイムデータAPI

公開JSONエンドポイントを通じてライブビジター数とリアルタイム分析データにアクセスします。これらのエンドポイントは、ウェブサイトでライブ統計を表示するために設計されており、認証は必要ありません。

概要

リアルタイムエンドポイントが提供するもの:

  • 即座にアクセス可能 - 認証は不要です
  • ライブアップデート - データは5~30秒ごとに更新されます
  • 低レイテンシー - Cloudflareのグローバルエッジネットワークから配信されます
  • CORS有効 - ブラウザから直接安全に呼び出せます

ベースURL

すべてのリアルタイムエンドポイントは以下で利用可能です:

https://api.zenovay.com/e

ライブビジター数

あなたのウェブサイト上の現在のビジター数を取得します:

GET/e/live/:trackingCode

ライブビジター数を取得

リクエストBash
curl -X GET 'https://api.zenovay.com/e/live/ZV_XXXXXXXXXXX'
レスポンス (200 OK)JSON
{
"liveCount": 42,
"timestamp": "2025-01-20T14:30:00Z"
}

レスポンスフィールド:

フィールドタイプ説明
liveCountnumber現在オンラインのビジター数(過去5分以内にアクティブ)
timestampstringデータのISO 8601タイムスタンプ

リアルタイム分析

ビジター、ページビュー、地理的データを含む包括的なリアルタイム分析を取得します:

GET/e/realtime/:websiteId

リアルタイム分析データを取得

リクエストBash
curl -X GET 'https://api.zenovay.com/e/realtime/ws_abc123'
レスポンス (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"
}

レスポンスフィールド:

フィールドタイプ説明
visitors.currentnumber現在オンラインのビジター数
visitors.todaynumber本日のユニークビジター総数
visitors.changenumber前日比の変化率(パーセンテージ)
pageViews.currentnumber過去5分間のページビュー数
pageViews.todaynumber本日のページビュー総数
countriesarray現在のビジター数上位の国
topPagesarray現在のビジター数上位のページ

ビジター統計

バウンスレートとセッション期間を含む詳細なビジター統計を取得します:

GET/e/stats/:trackingCode

ビジター統計を取得

リクエストBash
curl -X GET 'https://api.zenovay.com/e/stats/ZV_XXXXXXXXXXX'
レスポンス (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"
}

レスポンスフィールド:

フィールドタイプ説明
visitors.livenumber現在オンラインのビジター数
visitors.todaynumber本日のユニークビジター数
visitors.weeknumber今週のユニークビジター数
visitors.monthnumber今月のユニークビジター数
bounceRatenumberバウンスレート (0-1)
avgSessionDurationnumber平均セッション時間(秒)
pagesPerSessionnumberセッションあたりの平均ページ数
newVisitorRatenumber新規ビジターの割合
returningVisitorRatenumberリターンビジターの割合

ウェブサイトのステータス

ウェブサイトのトラッキングが有効かどうかを確認します:

GET/e/:trackingCode/status

トラッキングステータスを確認

リクエストBash
curl -X GET 'https://api.zenovay.com/e/ZV_XXXXXXXXXXX/status'
レスポンス (200 OK)JSON
{
"active": true,
"tracking_code": "ZV_XXXXXXXXXXX",
"domain": "example.com",
"last_event": "2025-01-20T14:29:55Z"
}

JavaScript統合

基本的なライブカウンター

ウェブサイトにライブビジター数を表示します:

ライブビジターカウンターJavaScript
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);
  }
}
}

// 使用方法
const counter = new LiveVisitorCounter('ZV_XXXXXXXXXXX', 'visitor-count');
counter.start();

リアルタイムダッシュボード

ミニ分析ダッシュボードを構築します:

リアルタイムダッシュボードJavaScript
async function updateDashboard(websiteId) {
try {
  const response = await fetch(
    `https://api.zenovay.com/e/realtime/${websiteId}`
  );
  const data = await response.json();

  // ビジター数を更新
  document.getElementById('live-visitors').textContent = data.visitors.current;
  document.getElementById('today-visitors').textContent = data.visitors.today.toLocaleString();

  // ページビュー数を更新
  document.getElementById('live-pageviews').textContent = data.pageViews.current;

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

  // 上位のページを更新
  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);
}
}

// 30秒ごとに更新
setInterval(() => updateDashboard('ws_abc123'), 30000);
updateDashboard('ws_abc123');

React Hook

リアルタイムデータ用のカスタムReactフック:

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

// コンポーネントでの使用方法
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:

useRealtimeAnalytics ComposableTypeScript
import { ref, onMounted, onUnmounted } 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 };
}

レート制限

リアルタイムエンドポイントには以下のレート制限があります:

エンドポイントレート制限キャッシュTTL
/e/live/:trackingCode1000リクエスト/分5秒
/e/realtime/:websiteId500リクエスト/分10秒
/e/stats/:trackingCode500リクエスト/分30秒
/e/:trackingCode/status100リクエスト/分60秒

データはエッジでキャッシュされます。キャッシュTTL内の複数のリクエストは同じデータを受け取るため、クライアント側のコードから頻繁にポーリングすることが安全です。

CORS設定

すべてのリアルタイムエンドポイントはCORSをサポートしており、ブラウザから直接呼び出すことができます:

CORSヘッダーTEXT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type

エラーハンドリング

エラーハンドリングの例JavaScript
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
  try {
    const response = await fetch(url);

    if (response.status === 429) {
      // レート制限 - 待機して再試行
      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;
    // 指数バックオフ
    await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
  }
}
}

// 使用方法
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);
}

ベストプラクティス

  1. ローカルにキャッシュ - 高速なUI更新中の不要なリクエストを避けるため、メモリにデータを保存します
  2. 更新を遅延 - キャッシュTTLより速くUI更新をトリガーしないでください
  3. オフライン対応 - ネットワークが利用できない場合、タイムスタンプ付きの古いデータを表示します
  4. 変更をアニメーション化 - カウント変更時のスムーズなトランジションでUIがより反応的に感じられます
  5. 読み込み状態を表示 - 初期フェッチ中はスケルトンローダーを表示します

次のステップ

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