Skip to content
karim.semaan(open to work)
WorkExperienceAboutSkillsContactResume ↓
← All work
LolStats preview
Full-stackCompleted2024

LolStats

League match & live-game tracker

Survived Riot's breaking API migrations: account lookup ported to Riot IDs (Name#TAG), live games moved to Spectator v5 with a v4 fallback, and a summonerID recovery path through recent match data when the Summoner endpoint dropped the field.

Riot ID lookup · match history + live gameSynthetic preview · no Riot data

Three invented players, generated in your browser from a fixed seed. The real app accepted any Name#TAG across 16 regions.

Pick a synthetic Riot ID to render its last 5 matches and run a live-game check.

Synthetic preview. No real data: the players, matches, champions, and ranks are invented and generated locally from a fixed seed, so the same lookup always shows the same result. The real app called the Riot API: Account v1 + Summoner v4 to resolve the player, Match v5 for the last 20 games, and Spectator v5 (v4 fallback) for live games, across 16 regions.

Synthetic preview. Players, matches, and ranks are invented and seeded locally; no Riot API calls and no real player data.

LolStats is a server-rendered Flask + Jinja app over the Riot Games API. Given a Riot ID (Name#TAG) and one of 16 regions, it resolves the account (Account v1, then Summoner v4) and renders the 20 most recent matches (Match v5) with per-player KDA, CS per minute, vision score and wards, damage dealt and taken, the full item build, keystone and secondary rune, and summoner spells, plus queue-type labels and remake detection for games under four minutes. A live-game view hits Spectator v5 with a v4 fallback and fans out over a 10-worker thread pool to pull every participant's ranked solo tier, LP and win rate. Match details are fetched concurrently too, and a requests-cache SQLite store with a 1-hour TTL caches only HTTP 200 responses, so rate-limit errors are never pinned. Artwork resolves from Riot's Data Dragon CDN against each region's current patch version. The code also wears Riot's API churn honestly: when the Summoner endpoint stopped returning summoner ids, a recovery path digs the id out of recent match data instead.

  • Python
  • Flask
  • Jinja2
  • Riot Games API
  • requests-cache
  • SQLite
  • concurrent.futures
Riot APIs
Account · Match v5 · Spectator · League v4
Match depth
Last 20
Regions
16
Caching
1h SQLite

What I'd improve

Rate limiting is reactive: a 429 surfaces as an error message instead of being absorbed by a real request budget, and the flat 1-hour cache TTL is a blunt instrument next to Riot's per-endpoint limits. I would add a proper limiter with backoff, replace the print-based debug tracing with structured logging, and build the rune-icon lookup table once instead of re-parsing the full runes JSON for every icon. The scratch /test route and test_api.py would become a real test suite with recorded API fixtures.

Request access
Want something like this? Get in touch →

Measured:30/30 chatbot evals·20-case Bastion eval·Serving·Calibration Lab

© 2026 Karim SemaanBuilt with Next.js, Tailwind & Supabase.LinkedIn ↗︎GitHub ↗︎