PandaScore API Alternative: Free Live Esports Odds for CoDL, LoL, CS2, Dota & Valorant
You Searched PandaScore. Here’s What They Don’t Tell You.
You’re evaluating the PandaScore esports API. Maybe you’re building a bot for League of Legends, a model for CS2, or a Call of Duty League dashboard. Before you sign up, two things you need to know — and one of them will probably end the conversation.
One: PandaScore’s stats plans explicitly prohibit betting-related usage. It’s in their terms. If your project touches odds, lines, or anything resembling a wager, you are not allowed to use PandaScore — full stop.
Two: PandaScore’s pricing is per video game. A multi-esport project covering LoL, CS2, Dota 2, and Valorant isn’t €400/month — it’s €1,600/month at the historical tier, or €4,000/month if you want live data. Per game. Per month.
If you’re building a stats-only product (heatmaps, player K/D, scrim analysis), PandaScore is still the strongest pure-play stats API on the market and worth the money. If you’re building anything betting-adjacent — odds comparison, line shopping, value scanners, arb bots, prediction-market hedging — PandaScore is closed to you. Read on for what to use instead.
What PandaScore Actually Gives You
Let’s be fair. PandaScore is a serious product with deep esports stats coverage across 20+ titles, a clean REST API, and decent docs. Their core offering is match stats: kills, deaths, rounds won, gold differentials, draft picks, hero/agent selections, K/D/A — the granular telemetry you need for stats-driven products.
| PandaScore Tier | Price | Includes | Rate Limit |
|---|---|---|---|
| Free | €0 | Static data, calendar, pre-match only. No live, no historical. | 1,000 req/hr |
| Historical & Post-Match | From €400/mo per game | Post-match results, historical stats, all fixture features | 10,000 req/hr |
| Live Basic | From €1,000/mo per game | WebSocket, limited live stats (LoL & CS2 only), replay API | 10,000 req/hr |
| Live Pro | Contact sales | Full live API, all stats, play-by-play feed | 10,000 req/hr |
Notice what’s missing from every single tier? Bookmaker odds. PandaScore has never been an odds API. They aggregate stats and timelines, not lines from sportsbooks. Their team has been clear about this — the betting prohibition isn’t a policy choice, it’s because the product was never built for it.
The Per-Game Pricing Trap
PandaScore’s per-video-game billing is the part most developers miss until checkout. Here’s the math for a realistic multi-esport project:
| Coverage | Historical Tier | Live Basic Tier |
|---|---|---|
| LoL only | €400/mo | €1,000/mo |
| LoL + CS2 | €800/mo | €2,000/mo |
| LoL + CS2 + Dota 2 | €1,200/mo | €3,000/mo |
| LoL + CS2 + Dota 2 + Valorant | €1,600/mo | €4,000/mo |
| + Rocket League, R6, Overwatch | €2,800/mo | €7,000/mo |
Annual subscriptions get a 10% discount. You’re still looking at €17,000+/year for a real multi-game live stats setup. And again — you can’t legally use any of it for a betting product.
Call of Duty League: The PandaScore Question
This is the most-searched PandaScore query and it deserves a direct answer. PandaScore covers the Call of Duty League with deep stats — kills, K/D, map performance, round-by-round telemetry. They’re the strongest API on the market for that.
What they don’t cover is betting odds on those CDL matches. Their stats plans explicitly prohibit it, and even if they didn’t, sportsbook prices aren’t part of the product. So if you came searching for “PandaScore Call of Duty League API” because you want to build a model that prices CDL matches, scan for value, or run line shopping across books, PandaScore is the wrong tool.
OddsPapi covers CDL fixtures (sport ID 56) the same way it covers LoL and CS2 — match winner, map markets, multi-bookmaker pricing — and you can pull every Toronto Koi, Optic Texas, and Cloud9 New York match through the same /v4/odds endpoint shown below. One free key, every esport, no per-game fees.
The Alternative: OddsPapi for Esports Odds
If you came to PandaScore looking for betting data rather than stats, you want OddsPapi instead. OddsPapi aggregates real-time and historical odds from 350+ bookmakers across LoL, CS2, Dota 2, Valorant, Call of Duty League, and seven other esports — and the terms explicitly support betting use cases. That’s the entire point of the product.
| Feature | PandaScore | OddsPapi |
|---|---|---|
| Data type | Match stats (kills, K/D, rounds) | Bookmaker odds (live + historical) |
| Betting use allowed | No (prohibited in TOS) | Yes (built for it) |
| Free tier | Pre-match metadata only | Full odds + historical, free |
| Live data | From €1,000/mo per game | Free tier (REST), Pro tier (WebSocket) |
| Historical data | From €400/mo per game | Free tier |
| Bookmakers covered | 0 (not an odds API) | 50+ per major LoL/CS2 fixture, 350+ overall |
| Esports titles | 20+ (stats) | 12 (odds): LoL, CS2, Dota 2, Valorant, CoD, Rocket League, Overwatch, R6, StarCraft, AoV, KoG, HoK |
| Multi-game cost | €2,000+/mo for 5 esports | €0 for all 12 esports |
Stats and odds are different products. PandaScore wins on stats. OddsPapi wins on odds. If you need both for the same project, the working pattern is to use them side by side — PandaScore for the player telemetry, OddsPapi for the lines — but only if you have a non-betting use case for the PandaScore data.
Tutorial: Pulling Live Esports Odds with Python
Here’s how to get LoL, CS2, Dota 2, Valorant, and Call of Duty League odds working in under 20 lines of Python. No credit card, no sales call. Grab a free key from oddspapi.io first. For the deeper esports walkthrough — markets, map handicaps, multi-tournament filtering — see our esports odds API guide.
Step 1: Authentication
OddsPapi uses an apiKey query parameter, not a header. Every request needs it.
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.oddspapi.io/v4"
# Test the connection
r = requests.get(f"{BASE_URL}/sports", params={"apiKey": API_KEY})
print(r.status_code) # 200 = you're in
Step 2: Discover the Esports Sport IDs
Each esports title is its own sport with its own ID. Pull them dynamically rather than hardcoding — the catalog grows.
sports = requests.get(f"{BASE_URL}/sports", params={"apiKey": API_KEY}).json()
esports = [s for s in sports if s["slug"].startswith("esport-")]
for e in esports:
print(f"{e['sportId']:>3} {e['slug']:<32} {e['sportName']}")
# Output:
# 16 esport-dota ESport Dota
# 17 esport-counter-strike ESport Counter-Strike
# 18 esport-league-of-legends ESport League of Legends
# 56 esport-call-of-duty ESport Call of Duty
# 57 esport-overwatch ESport Overwatch
# 59 esport-rocket-league ESport Rocket League
# 61 esport-valorant ESport Valorant
# ... (12 esports total)
Step 3: Pull Upcoming LoL Fixtures
The /fixtures endpoint takes a sport ID and a date range (max 10 days). Each fixture has a hasOdds flag — only those will return prices when you call /odds next.
from datetime import date, timedelta
today = date.today().isoformat()
end = (date.today() + timedelta(days=7)).isoformat()
r = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY,
"sportId": 18, # League of Legends
"from": today,
"to": end,
})
fixtures = r.json()
with_odds = [f for f in fixtures if f.get("hasOdds")]
print(f"LoL fixtures: {len(fixtures)} total, {len(with_odds)} with odds")
for f in with_odds[:5]:
print(f" {f['tournamentName']}: {f['participant1Name']} vs {f['participant2Name']}")
# Output:
# LoL fixtures: 135 total, 85 with odds
# LEC: G2 Esports vs Team Vitality
# LCS: 100 Thieves vs Cloud9
# LCK: T1 vs Gen.G
# LPL: JD Gaming vs Bilibili Gaming
# Esports World Cup: Fnatic vs Hanwha Life Esports
Step 4: Pull Live Odds for One Fixture
The /odds endpoint returns the current price across every bookmaker that's pricing the fixture. The response is heavily nested — read it carefully.
fixture_id = with_odds[0]["fixtureId"]
odds = requests.get(f"{BASE_URL}/odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id,
"bookmakers": "1xbet,stake,betway,draftkings,polymarket",
}).json()
# bookmakerOdds[slug][markets][market_id_str][outcomes][outcome_id_str][players]['0']['price']
for slug, book in odds["bookmakerOdds"].items():
for mid, market in book["markets"].items():
for oid, outcome in market["outcomes"].items():
price = outcome["players"]["0"]["price"]
print(f" {slug:<12} market {mid} outcome {oid} -> {price}")
That's the live snapshot for one fixture across five books. Swap in any of OddsPapi's 350+ bookmakers — sharp books, soft books, crypto books, prediction markets — by passing them as a comma-separated list.
Step 5: Multi-Game Scan
The whole reason to switch from a per-game API is being able to loop across every esport in one script without four separate billing relationships:
import time
ESPORTS = {
18: "League of Legends",
17: "Counter-Strike",
16: "Dota 2",
61: "Valorant",
56: "Call of Duty",
}
today = date.today().isoformat()
end = (date.today() + timedelta(days=7)).isoformat()
for sport_id, name in ESPORTS.items():
r = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY, "sportId": sport_id,
"from": today, "to": end,
})
fixtures = r.json() if r.status_code == 200 else []
with_odds = [f for f in fixtures if f.get("hasOdds")]
print(f"{name:<20} {len(with_odds):>3} priced fixtures (next 7 days)")
time.sleep(0.3) # rate-limit politeness
Output during a busy esports week:
League of Legends 85 priced fixtures (next 7 days)
Counter-Strike 10 priced fixtures (next 7 days)
Dota 2 6 priced fixtures (next 7 days)
Valorant 35 priced fixtures (next 7 days)
Call of Duty 18 priced fixtures (next 7 days)
That's 154 priced fixtures across five esports. Through PandaScore at the live tier, that same coverage costs €5,000/month. Through OddsPapi: free.
Step 6: Historical Odds for Backtesting
If you're building a model, you need historical price movements to train against. PandaScore charges €400/month per game for historical access. OddsPapi gives it away on the free tier through /historical-odds (max 3 bookmakers per call):
history = requests.get(f"{BASE_URL}/historical-odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id,
"bookmakers": "1xbet,stake,betway",
}).json()
# /historical-odds returns players["0"] as a LIST of snapshots, not a dict
for slug, book in history["bookmakers"].items():
for mid, market in book["markets"].items():
for oid, outcome in market["outcomes"].items():
snapshots = outcome["players"]["0"]
print(f"{slug} market {mid} outcome {oid}: {len(snapshots)} price points")
for snap in snapshots[:3]:
print(f" {snap['createdAt']} -> {snap['price']}")
Loop that across every priced fixture from a past split and you have a full backtesting dataset — for free.
When to Pick Which
| Your Project | Pick |
|---|---|
| Esports stats site, scrim analysis, player heatmaps, fantasy app | PandaScore (the only serious option) |
| Odds comparison, value scanner, arb bot, betting model | OddsPapi (PandaScore is contractually closed to you) |
| Backtesting historical betting strategies | OddsPapi (free historical, no per-game fees) |
| Live betting bot reacting to in-play moves | OddsPapi WebSocket (Pro tier) |
| Call of Duty League stats (kills, K/D, telemetry) | PandaScore (the only deep CDL stats source) |
| Call of Duty League odds & line shopping | OddsPapi (PandaScore prohibits betting use) |
| Multi-esport project on a budget | OddsPapi (one key, every game, no per-game billing) |
Stop Budgeting €5,000/Month for Esports Data
If your use case is anything betting-adjacent — and "anything betting-adjacent" is the use case PandaScore explicitly bans — you don't need to negotiate with their sales team. You don't need an annual contract. You don't need to pay per video game.
Grab a free OddsPapi key, paste the snippets above into a Python file, and you'll have live odds from 50+ bookmakers across LoL, CS2, Dota 2, Valorant, and the Call of Duty League running in under five minutes.