Back to blog

How the Dota 2 Stats Pages Work

By Amine Amine

nextjsdatadota2

The Dota 2 section of this site shows win rates, counter picks, item builds and laning tips for every hero. There's no database behind it and no scraping. It's a thin layer over OpenDota's public API, plus a bit of arithmetic and a lot of caching.

The data

Four OpenDota endpoints do all the work:

  • heroStats: every hero, with pick and win counts per rank bracket (Herald through Immortal);
  • heroes/{id}/matchups: one hero's record against every other hero;
  • heroes/{id}/itemPopularity: which items get bought, and when;
  • constants/items: names, costs and images for items.

Win rate and pick rate

OpenDota splits stats into eight rank brackets. I sum wins and picks across all of them, then divide:

let totalWins = 0;
let totalPicks = 0;
for (let i = 1; i <= 8; i++) {
  totalWins += h[`${i}_win`];
  totalPicks += h[`${i}_pick`];
}
const winRate = totalPicks > 0 ? (totalWins / totalPicks) * 100 : 0;

Pick rate is each hero's picks as a share of all picks. It's the same idea one level up, and it tells you whether a hero is a pocket pick or played everywhere.

Counters that mean something

A raw "win rate against X" is misleading on its own. A hero that wins 55% of all games will also win more than 50% against most opponents. So instead of raw win rate, each matchup shows the advantage: how much better or worse the hero does against that opponent than it does overall.

const advantage = winRateVsThem - heroOverallWinRate;

Matchups with fewer than 10 games are dropped, because three lucky games shouldn't make a hero a "hard counter".

The same numbers power the home page card, "best win rate today". That one also requires at least 10,000 picks, so a niche hero on a hot streak doesn't take the crown.

Caching: fast for you, polite to OpenDota

OpenDota is free and rate-limited, and these numbers change slowly. Every request goes through Next.js's fetch cache:

  • hero stats, matchups and item popularity are revalidated every hour;
  • item constants are revalidated every day.

Most visitors therefore get a cached page, and OpenDota sees a handful of requests an hour instead of one per visitor. The sitemap uses the same cached hero list to give search engines a page per hero.

What I'd add next

A "draft helper" already suggests heroes that counter the enemy's picks. Next, I'd like to weight it by role, since a counter that doesn't fit your team's missing position isn't much help.

Go find your hero, or press ⌘K and type their name.

How the Dota 2 Stats Pages Work | SimpleCode by Amine Amine