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 20,000 requests a month. 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. Backfilling history covers pacing the walk.