Prefer to chat? Query the API with natural language using our AI Skill →

Give your AI tool live Cito endpoint context before you write integration code.

Open AI Skill

Public docs assistant

Tell Cito API what you are building.

Get endpoint recommendations, exact requests, code snippets, and a free-key CTA without digging through every page.

Guides/Real-time Match Tracking

Real-time Match Tracking

Build live match trackers with real-time updates

This guide shows you how to build a real-time match tracker that updates as games progress. We'll cover both polling and webhook approaches.

Approach 1: Polling

Poll the API at regular intervals to get the latest match data:

match-tracker.js
class MatchTracker {
  constructor(apiKey, matchId) {
    this.apiKey = apiKey;
    this.matchId = matchId;
    this.lastUpdate = null;
    this.listeners = [];
  }

  subscribe(callback) {
    this.listeners.push(callback);
    return () => {
      this.listeners = this.listeners.filter(l => l !== callback);
    };
  }

  emit(event, data) {
    this.listeners.forEach(cb => cb(event, data));
  }

  async start() {
    this.interval = setInterval(async () => {
      try {
        const response = await fetch(`https://api.citoapi.com/api/v1/cod/matches/${this.matchId}`, {
          headers: { 'x-api-key': this.apiKey }
        });
        const match = await response.json();

        if (this.hasChanged(match)) {
          this.emit('update', match);
          this.lastUpdate = match;
        }
      } catch (error) {
        this.emit('error', error);
      }
    }, 5000); // Poll every 5 seconds
  }

  hasChanged(newData) {
    if (!this.lastUpdate) return true;
    return JSON.stringify(newData) !== JSON.stringify(this.lastUpdate);
  }

  stop() {
    clearInterval(this.interval);
  }
}

// Usage
const tracker = new MatchTracker(process.env.CITO_API_KEY, 'bp-match-214935');

tracker.subscribe((event, data) => {
  if (event === 'update') {
    console.log('Match updated:', data);
    updateUI(data);
  }
});

tracker.start();

Approach 2: Webhooks (Recommended)

Use webhooks for instant updates without polling:

// Register webhook for match events
const webhook = await cito.webhooks.create({
  url: 'https://yourapp.com/webhook',
  events: ['match.updated', 'match.ended', 'player.elimination']
});

// Handle incoming webhooks
app.post('/webhook', (req, res) => {
  const { event, data } = req.body;

  // Broadcast to connected clients via WebSocket
  io.emit('match-update', { event, data });

  res.sendStatus(200);
});

React Integration

useMatchTracker.ts
import { useState, useEffect } from 'react';
import { io } from 'socket.io-client';

export function useMatchTracker(matchId: string) {
  const [match, setMatch] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Initial fetch
    fetch(`/api/matches/${matchId}`)
      .then(res => res.json())
      .then(data => {
        setMatch(data);
        setLoading(false);
      })
      .catch(setError);

    // Subscribe to real-time updates
    const socket = io();

    socket.on('match-update', ({ event, data }) => {
      if (data.match_id === matchId) {
        setMatch(prev => ({ ...prev, ...data }));
      }
    });

    return () => socket.disconnect();
  }, [matchId]);

  return { match, loading, error };
}

// Usage in component
function MatchView({ matchId }) {
  const { match, loading, error } = useMatchTracker(matchId);

  if (loading) return <Spinner />;
  if (error) return <Error message={error.message} />;

  return (
    <div>
      <h1>{match.tournament}</h1>
      <div>Game {match.current_game} of {match.total_games}</div>
      <Leaderboard players={match.players} />
    </div>
  );
}

Best Practices

Cache aggressively

Store match data locally and only update changed fields

Use diff updates

Only re-render components that have changed data

Handle reconnection

Implement reconnection logic for WebSocket disconnects