Migrate from a scraper
Mapping the concepts you already have onto this API, and what changes.
If you're arriving from a scraped HTML pipeline, most of the work is mapping vocabulary. Here's the translation.
Concept mapping
| Scraper concept | Here |
|---|---|
| Match page URL | id (UUIDv7) or a list filter |
| Team page slug | slug — works directly as a path id |
| Event page | tournament_id, or ?tournament= on the match list |
| Scoreboard table | /matches/{id}/stats |
| Round history graphic | /matches/{id}/rounds |
| Map veto box | /matches/{id}/vetoes |
| Rating column | rating on a stat line — ours, not a third party's |
| Odds comparison table | A single de-vigged line; per-book prices are not served |
What gets easier
No parsing, no breakage on redesign. Field names are a versioned contract locked by tests.
Pagination that doesn't skip rows. Cursors are stable under concurrent inserts, unlike
?page=N over a table that's being written to.
Coverage is declared. data_available on each match tells you which layers exist, and
/v1/cs2/coverage states what the product does and doesn't have — including what's not built yet.
Change is pushed. A WebSocket subscription replaces a polling loop entirely.
What's different
No per-book odds. The API serves one de-vigged aggregate line (eo_market) with a book_count,
never individual bookmaker prices or names. If your pipeline compared books, that's not something
this API replaces — see the market line.
Requests are metered. A scraper's cost is bandwidth; here it's 50,000 requests a month on Standard and 150,000 on Pro. The habit to unlearn is polling everything on one timer. See budget your requests.
rating is ours. It is not the number you scraped from elsewhere and won't match it. Compare
trends, not absolute values.
logo_url is always empty. We hold no redistribution rights to team marks.
A first migration
Map your entities. Walk /v1/cs2/teams and /v1/cs2/players once at limit=500 and store the
UUIDs against whatever keys you already use. A few requests, done weekly.
Replace your fixture scrape with a cached /v1/cs2/matches window — see
build a fixtures cache. The list row already carries team and
tournament names, so most schedule rendering needs nothing else.
Replace per-match scrapes with ?include=teams,tournament,odds plus the detail layers you
actually use, gated on data_available.
Replace your live poller with a WebSocket subscription. This is usually the biggest single saving.
Backfilling what you already have
You probably have history you don't want to re-fetch. Match ids won't line up with your old keys, so
join on (scheduled_at, team names) for a one-off reconciliation, then store our UUIDs and use them
from then on. Pulling the historical archive covers the bulk endpoint that makes
the walk cheap.