The VCT Dataset

Every page on this site is a view over one file — and this is the file.

Download vct.duckdb

8 tables · rebuilt daily · last updated

A permanent URL that always resolves to the current build, so it is safe to cite or script against.

A single DuckDB database containing every tier 1 VCT match since 2021 — all international events (Masters, Champions) plus the regional events that qualify into them. Series, maps, rounds, per-player map stats, kill matchups and clutch/multikill events, refreshed daily. Free to use, including commercially, with no warranty; because the data is derived from publicly available professional match pages, the Terms of Service are the statement of what you're agreeing to rather than an off-the-shelf licence identifier. A link back to vct-reference.com is appreciated.

One caveat before you trust a number: coverage is uneven — China has no economy data at all and only ~8% of its maps carry the multikill/clutch and kill-matrix blocks, and side splits are missing on some maps everywhere. Gate on maps.performance_available / economy_available and divide side stats by side-gated denominators, or the gaps read as zeros. Full figures on Methodology.

Tables

matches
One row per series — teams, event, stage, format, result, true-UTC timestamp.
maps
One row per map played, with the round score and which optional stat blocks were available.
rounds
One row per round — winner, side, how it ended, and the economy snapshot where recorded.
player_map
One row per player per map — rating, ACS, ADR, KAST, HS%, K/D/A, opening duels, multikills and clutches, each split overall / attack / defense.
kill_matrix
One row per player pair per map — who killed whom, including Operator and opening kills.
notables
One row per multikill or clutch event, with the round and the opponents involved.
players
One row per player — stable id, handle, country.
teams
One row per team — stable id, name, country.

Examples

Both run as-is in the DuckDB CLI (duckdb vct.duckdb) or the Python package.

Career kills leaderboard

SELECT p.player_name, SUM(pm.kills_all)::INT AS kills
FROM player_map pm
JOIN players p USING (player_id)
JOIN matches m USING (match_id)
WHERE m.listing_status = 'Completed' AND m.is_showmatch = FALSE
GROUP BY 1 ORDER BY kills DESC LIMIT 25;

Clutches per map, coverage-gated

SELECT p.player_name,
       ROUND(AVG(pm.clutch_1v1 + pm.clutch_1v2 + pm.clutch_1v3
               + pm.clutch_1v4 + pm.clutch_1v5), 3) AS per_map
FROM player_map pm
JOIN players p  USING (player_id)
JOIN maps    mp USING (match_id, game_id)
WHERE mp.performance_available   -- without this, unscraped maps count as zeros
GROUP BY 1 HAVING COUNT(*) >= 100
ORDER BY per_map DESC LIMIT 25;