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/Leaderboard Systems

Leaderboard Systems

Build ranked leaderboards with pagination and filters

Fetching Leaderboard Data

// Get regional leaderboard
const { data, pagination } = await cito.fortnite.leaderboards.get('NA-EAST', {
  limit: 100,
  page: 1
});

// Response
{
  "data": [
    {
      "rank": 1,
      "username": "Bugha",
      "points": 45200,
      "wins": 23,
      "eliminations": 847,
      "kd_ratio": 4.21,
      "matches": 156,
      "change": 2  // Rank change from yesterday
    },
    // ... more players
  ],
  "pagination": {
    "page": 1,
    "total_pages": 50,
    "total_results": 5000
  }
}

Leaderboard Component

Leaderboard.tsx
function RankChange({ change }: { change: number }) {
  if (change > 0) return <span className="text-green-500">▲ {change}</span>;
  if (change < 0) return <span className="text-red-500">▼ {Math.abs(change)}</span>;
  return <span className="text-gray-500">—</span>;
}

export function Leaderboard({ region, game }: Props) {
  const [page, setPage] = useState(1);

  const { data, isLoading } = useQuery({
    queryKey: ['leaderboard', game, region, page],
    queryFn: () => cito[game].leaderboards.get(region, { page, limit: 50 })
  });

  return (
    <div>
      <div className="flex items-center justify-between mb-4">
        <h2>{region} Leaderboard</h2>
        <RegionSelector value={region} onChange={setRegion} />
      </div>

      <table className="w-full">
        <thead>
          <tr>
            <th className="text-left">Rank</th>
            <th className="text-left">Player</th>
            <th className="text-right">Points</th>
            <th className="text-right">Wins</th>
            <th className="text-right">K/D</th>
            <th className="text-right">Change</th>
          </tr>
        </thead>
        <tbody>
          {data?.data.map((player) => (
            <tr key={player.username}>
              <td className="font-bold">
                {player.rank <= 3 ? ['🥇', '🥈', '🥉'][player.rank - 1] : player.rank}
              </td>
              <td>
                <Link href={`/player/${player.username}`}>
                  {player.username}
                </Link>
              </td>
              <td className="text-right">{player.points.toLocaleString()}</td>
              <td className="text-right">{player.wins}</td>
              <td className="text-right">{player.kd_ratio.toFixed(2)}</td>
              <td className="text-right">
                <RankChange change={player.change} />
              </td>
            </tr>
          ))}
        </tbody>
      </table>

      <Pagination
        page={page}
        totalPages={data?.pagination.total_pages}
        onChange={setPage}
      />
    </div>
  );
}

Player Search

Find a specific player's rank:

// Search for player in leaderboard
const { data } = await cito.fortnite.leaderboards.search('NA-EAST', {
  username: 'Bugha'
});

// Response
{
  "rank": 1,
  "username": "Bugha",
  "points": 45200,
  "percentile": 0.01  // Top 0.01%
}

// Component
function PlayerRankSearch({ region }) {
  const [search, setSearch] = useState('');
  const [result, setResult] = useState(null);

  const handleSearch = async () => {
    const { data } = await cito.fortnite.leaderboards.search(region, {
      username: search
    });
    setResult(data);
  };

  return (
    <div>
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search player..."
      />
      <button onClick={handleSearch}>Search</button>

      {result && (
        <div className="mt-4 p-4 bg-secondary">
          <p>
            {result.username} is ranked <strong>#{result.rank}</strong>
          </p>
          <p className="text-muted">
            Top {(result.percentile * 100).toFixed(2)}%
          </p>
        </div>
      )}
    </div>
  );
}

Performance: Virtual Scrolling

For large leaderboards, use virtual scrolling:

import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualLeaderboard({ players }) {
  const parentRef = useRef(null);

  const virtualizer = useVirtualizer({
    count: players.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 48, // Row height
  });

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div style={{ height: virtualizer.getTotalSize() }}>
        {virtualizer.getVirtualItems().map((virtualRow) => {
          const player = players[virtualRow.index];
          return (
            <div
              key={player.username}
              style={{
                position: 'absolute',
                top: virtualRow.start,
                height: virtualRow.size,
              }}
            >
              <LeaderboardRow player={player} />
            </div>
          );
        })}
      </div>
    </div>
  );
}