Map results
Per-map scores, the half-by-half split, and why overtime is null rather than zero.
GET /v1/{game}/matches/{id}/maps returns one row per map played, ordered by map_number.
A bo3 has up to 3 rows and a bo1 exactly 1 — but a series that ended early has fewer rows than
its format allows, so never assume a count from format.
Scores
score_a and score_b are final rounds won by team A and team B (the parent match's team_a_id /
team_b_id), including overtime.
map_name is always the Valve identifier — de_ancient, de_mirage — never a display name.
The half split
first_half_a/first_half_b and second_half_a/second_half_b break the map into halves, and are
null together when the split isn't available for that map. The final score is authoritative
regardless.
Overtime is null, not zero
overtime_a/overtime_b are null when the map didn't go to overtime — deliberately, so you
can distinguish "no overtime" from "overtime happened and this team won no rounds in it". Treating
null as 0 collapses that distinction.
maps = get(f"/v1/cs2/matches/{match_id}/maps")["data"]
for m in maps:
ot = "" if m["overtime_a"] is None else f" (OT {m['overtime_a']}-{m['overtime_b']})"
print(f"Map {m['map_number']}: {m['map_name']} {m['score_a']}-{m['score_b']}{ot}")Reconciling with the series score
The match's score_a/score_b count maps won, not rounds. So a 2–1 series with map scores
13–8, 10–13, 13–11 gives score_a: 2, score_b: 1. Mixing the two is a common source of confusing
totals.
Which side started where
Not exposed. The half-by-half columns tell you the shape of the map without telling you which side each team opened on — that's held internally and isn't part of the customer contract today.