Build a League of Legends Fantasy Esports App with Real-Time Data
How to build a fantasy LoL platform with player projections, live scoring, and optimal lineup suggestions using the Cito API.
Introduction: Fantasy LoL Is Massive
Fantasy esports is one of the fastest-growing segments in gaming. Millions of LoL fans already pick their favorite pros for weekly fantasy matchups, and the demand for better tools keeps growing.
In this guide, you'll learn how to build a fantasy League of Legends platform with:
- Player projections based on historical stats
- Live scoring that updates during matches
- Optimal lineup suggestions
- Salary cap and draft mechanics
Whether you're building a full fantasy platform or adding fantasy features to your Discord server, this guide covers the API integration you need.
The Fantasy LoL Data Model
Every fantasy platform needs these core data points:
Player Fantasy Stats
The stats that generate fantasy points:
- Kills: +3 points
- Deaths: -1 point
- Assists: +2 points
- CS (per 100): +1 point
- Triple kills: +2 bonus
- Quadra kills: +5 bonus
- Penta kills: +10 bonus
- Win: +2 points
What You Need From the API
- Player career stats (for projections)
- Live game stats (for scoring)
- Upcoming schedule (for lineup deadlines)
- Team matchups (for strength-of-schedule)
The Cito API has dedicated fantasy endpoints for exactly this.
Getting Started
mkdir lol-fantasy
cd lol-fantasy
npm init -y
npm install axios dotenv
// api.js
const axios = require('axios');
require('dotenv').config();
const api = axios.create({
baseURL: 'https://api.citoapi.com/api/v1/lol',
headers: { 'Authorization': Bearer ${process.env.CITO_API_KEY} },
});
module.exports = api;
Fetching Fantasy Projections
The Cito API provides pre-calculated fantasy projections:
const api = require('./api');async function getProjections() {
const { data } = await api.get('/fantasy/projections/players');
return data;
}
async function showTopProjections() {
const projections = await getProjections();
console.log('Top Fantasy Projections This Week:\n');
console.log('Player | Team | Proj | Salary');
console.log('----------------|---------|-------|-------');
projections.data
.sort((a, b) => b.projectedPoints - a.projectedPoints)
.slice(0, 20)
.forEach(p => {
console.log(
${p.name.padEnd(16)}| ${p.team.padEnd(8)}| ${p.projectedPoints.toFixed(1).padStart(5)} | $${p.salary}
);
});
}
showTopProjections();
Building the Scoring Engine
Calculate fantasy points from live game data:
const SCORING = {
kills: 3,
deaths: -1,
assists: 2,
cs100: 1, // per 100 CS
tripleKill: 2,
quadraKill: 5,
pentaKill: 10,
win: 2,
};
function calculatePoints(gameStats) {
let points = 0;
points += (gameStats.kills || 0) * SCORING.kills;
points += (gameStats.deaths || 0) * SCORING.deaths;
points += (gameStats.assists || 0) * SCORING.assists;
points += Math.floor((gameStats.cs || 0) / 100) * SCORING.cs100;
points += (gameStats.tripleKills || 0) * SCORING.tripleKill;
points += (gameStats.quadraKills || 0) * SCORING.quadraKill;
points += (gameStats.pentaKills || 0) * SCORING.pentaKill;
if (gameStats.win) points += SCORING.win;
return points;
}
// Example: Faker's game stats
const fakerGame = {
kills: 5, deaths: 1, assists: 8,
cs: 312, tripleKills: 1, quadraKills: 0,
pentaKills: 0, win: true
};
console.log(Fantasy points: ${calculatePoints(fakerGame)});
// kills(15) + deaths(-1) + assists(16) + cs(3) + triple(2) + win(2) = 37
Live Scoring During Matches
Update fantasy scores in real-time while games are being played:
async function getLiveFantasyScores(rosterPlayerIds) {
const { data } = await api.get('/live');
if (!data.data || data.data.length === 0) {
return { live: false, scores: [] };
}
const scores = [];
for (const match of data.data) {
// Get detailed game stats
const gameData = await api.get(/live/${match.gameId}/stats);
for (const player of gameData.data.players) {
if (rosterPlayerIds.includes(player.id)) {
scores.push({
playerId: player.id,
name: player.name,
team: player.team,
currentPoints: calculatePoints(player.stats),
stats: player.stats,
gameStatus: match.status,
});
}
}
}
return { live: true, scores };
}
// Check scores for your roster
const myRoster = ['faker', 'chovy', 'zeus', 'keria', 'peyz'];
const scores = await getLiveFantasyScores(myRoster);
if (scores.live) {
console.log('LIVE Fantasy Scores:\n');
scores.scores.forEach(s => {
console.log(${s.name} (${s.team}): ${s.currentPoints} pts);
console.log( K/D/A: ${s.stats.kills}/${s.stats.deaths}/${s.stats.assists}\n);
});
}
Optimal Lineup Generator
Find the best lineup under a salary cap:
async function getOptimalLineup() {
const { data } = await api.get('/fantasy/optimal');
return data;
}
async function showOptimalLineup() {
const optimal = await getOptimalLineup();
console.log('Optimal Fantasy Lineup:\n');
console.log(Total Projected: ${optimal.data.totalProjected.toFixed(1)} pts);
console.log(Salary Used: $${optimal.data.salaryUsed} / $${optimal.data.salaryCap}\n);
optimal.data.players.forEach(p => {
console.log([${p.role}] ${p.name} (${p.team}) - ${p.projected.toFixed(1)} pts - $${p.salary});
});
}
showOptimalLineup();
Custom Lineup Optimizer
If you want to build your own optimizer with constraints:
function optimizeLineup(players, salaryCap = 50000) {
const roles = ['TOP', 'JNG', 'MID', 'ADC', 'SUP'];
const byRole = {};
// Group players by role
roles.forEach(role => {
byRole[role] = players
.filter(p => p.role === role)
.sort((a, b) => (b.projectedPoints / b.salary) - (a.projectedPoints / a.salary));
});
// Greedy approach: pick best value per role
let lineup = [];
let remainingSalary = salaryCap;
roles.forEach(role => {
const affordable = byRole[role].filter(p => p.salary <= remainingSalary);
if (affordable.length > 0) {
lineup.push(affordable[0]);
remainingSalary -= affordable[0].salary;
}
});
return {
players: lineup,
totalProjected: lineup.reduce((sum, p) => sum + p.projectedPoints, 0),
salaryUsed: salaryCap - remainingSalary,
};
}
Fantasy Value Rankings
Find undervalued players — high projected points relative to salary:
async function getValueRankings() {
const { data } = await api.get('/fantasy/value');
return data;
}
async function showSleepers() {
const value = await getValueRankings();
console.log('Top Fantasy Sleepers (Best Value):\n');
value.data.slice(0, 10).forEach((p, i) => {
console.log(${i + 1}. ${p.name} (${p.team}) - ${p.projectedPoints.toFixed(1)} pts @ $${p.salary});
console.log( Value Score: ${p.valueScore.toFixed(2)} | Ownership: ${p.ownership}%\n);
});
}
showSleepers();
Player Matchup Analysis
Factor in opponent strength for better projections:
async function analyzeMatchup(playerId) {
const [stats, schedule] = await Promise.all([
api.get(/players/${playerId}/stats),
api.get('/schedule/week'),
]);
const player = stats.data.data;
// Find this player's upcoming match
const nextMatch = schedule.data.data.find(m =>
m.team1 === player.team || m.team2 === player.team
);
if (!nextMatch) return null;
const opponent = nextMatch.team1 === player.team
? nextMatch.team2 : nextMatch.team1;
// Get opponent's defensive stats
const opponentStats = await api.get(/teams/${opponent}/stats);
return {
player: player.name,
opponent,
playerKDA: player.kda,
opponentGamesAllowed: opponentStats.data.data.avgKillsAllowed,
projection: player.projectedPoints,
matchupBoost: opponentStats.data.data.avgKillsAllowed > 15 ? 'GOOD' : 'TOUGH',
};
}
Putting It All Together
A complete fantasy dashboard class:
class FantasyDashboard {
constructor(api) {
this.api = api;
}
async getWeeklyOverview() {
const [projections, optimal, value, schedule] = await Promise.all([
this.api.get('/fantasy/projections/players'),
this.api.get('/fantasy/optimal'),
this.api.get('/fantasy/value'),
this.api.get('/schedule/week'),
]);
return {
topProjections: projections.data.data.slice(0, 20),
optimalLineup: optimal.data,
sleepers: value.data.data.slice(0, 10),
matchesThisWeek: schedule.data.data.length,
};
}
async getPlayerCard(playerId) {
const [stats, fantasy, champions] = await Promise.all([
this.api.get(/players/${playerId}/stats),
this.api.get(/fantasy/stats/players/${playerId}),
this.api.get(/players/${playerId}/champions),
]);
return {
stats: stats.data,
fantasy: fantasy.data,
topChampions: champions.data.data?.slice(0, 5),
};
}
}
// Usage
const dashboard = new FantasyDashboard(api);
const overview = await dashboard.getWeeklyOverview();
console.log(${overview.matchesThisWeek} matches this week);
console.log(Best pick: ${overview.topProjections[0].name});
Fantasy for Discord
Add fantasy features to your Discord bot:
// !fantasy top — Show top projected players
// !fantasy lineup — Show optimal lineup
// !fantasy sleepers — Show undervalued players
// !fantasy player — Show player fantasy card
Check out our Discord bot tutorial for the full bot setup, then add these fantasy commands on top.
Get Started
The Cito API's fantasy endpoints make it straightforward to build a fantasy LoL platform:
The fantasy endpoints are available on all plans, including the free tier.
Get your free API key and start building your fantasy LoL platform today.
---
Related reading:
- LoL Esports API: Complete Developer Guide — All 102 endpoints explained
- Build a LoL Discord Bot in 10 Minutes — Add esports commands to Discord
- Riot API vs Third-Party APIs — Compare your data source options
Ready to Build?
Get your API key and start building with esports data in minutes.