Build a fixtures cache
Keep a local schedule in sync for a few hundred requests a month.
The fixture list changes about 6 times an hour across the whole corpus. A cache refreshed on a slow timer stays effectively current for a fraction of your allowance.
The shape
Fetch a forward window, upsert by id, and re-read on a slow interval.
import datetime as dt
def refresh_fixtures(db, days_ahead=14, days_back=2):
today = dt.date.today()
params = {
"date_from": (today - dt.timedelta(days=days_back)).isoformat(),
"date_to": (today + dt.timedelta(days=days_ahead)).isoformat(),
"limit": 500,
}
for match in paginate("/v1/cs2/matches", **params):
db.upsert_match(match) # keyed on match["id"]Include a couple of days back: a match that finished after your last refresh needs its final score
and winner_team_id picked up.
What it costs
A 16-day window is a few hundred matches — one or two pages at limit=500. Hourly that's ~720–1,440
requests a month, 4–7% of a 20,000 plan. Every 6 hours it's under 1%.
Compare with polling /matches every 5 minutes: 8,640 requests, 43% of the plan, for a list that
changed six times an hour.
Use the list row as-is
A list row already carries team_a_name, team_b_name, team_a_short, team_b_short and
tournament_name. You don't need to resolve teams separately to render a schedule — that's the main
reason to cache the list shape rather than detail rows.
It also carries data_available, so your cache knows which matches have round or depth data without
asking.
Detect what changed
updated_at moves whenever a match row changes. Store it and you can act only on real changes:
existing = db.get_match(match["id"])
if existing and existing["updated_at"] == match["updated_at"]:
continue # nothing changed; skip downstream workThat won't save you API requests — you fetched the page either way — but it saves recomputation and lets you fire notifications only on genuine transitions.
Live matches need a different cadence
A live match's score changes far faster than the fixture list. Two options:
Poll just the live ones, which is a much smaller query:
live = get("/v1/cs2/matches?status=live&limit=100")["data"]At one call a minute that's 43,200/month — too much on its own. Every 5 minutes it's 8,640.
Or open a WebSocket and subscribe to the ids you care about. One ticket mint, then score and odds changes are pushed at no further request cost. For live data this is almost always the right answer — see connect a WebSocket.
Suggested cadences
| What | Interval | Requests/month |
|---|---|---|
| Fixture window | Every 6 hours | ~120 |
| Live matches | WebSocket | ~30 (reconnect ticket mints) |
| Odds | Every 5 minutes, or WebSocket | 8,640 / ~0 |
That's a complete integration inside half the plan.