GGScore API: Played and Upcoming CS2 Matches Without Scraping
6/9/2026, 1:31:14 PM
Trackers, bots, and widgets need two feeds: match results (who won, maps, scores) and fixtures (what plays next). Scraping usually means two brittle HTML parsers. GGScore is a paid CS2 match data API — both played and upcoming matches over REST, one auth model, JSON you can map to UI.
You need two kinds of match data for a tracker, bot, or dashboard: what already happened (scores, maps, winners) and what is scheduled next (fixtures, events, start times). Scraping two different pages doubles your breakage surface. GGScore is a paid CS2 match data API that returns both played and upcoming matches as JSON over REST — authenticate, fetch, ship.
This guide covers what the API is for, how auth and plans work, and minimal integration patterns for each match type. Verify paths, field names, and quotas on ggscore.net docs before production.
What GGScore Is (and Is Not)
| GGScore API | Scraping scoreboard sites | |
|---|---|---|
| Data | Normalized JSON: teams, scores, events, countries | HTML + fragile selectors |
| Match types | Played results + upcoming fixtures | Often separate pages / formats |
| Auth | X-API-Key header | Cookies, captchas, geo blocks |
| Cost | Paid tiers (Free to prototype) | Your time every layout change |
| Maintenance | Schema + docs | DOM archaeology |
GGScore is not a free public dump. It is a developer product: you pay for reliable structure and rate limits you can design against.
Public surface (per docs): countries, played matches, upcoming matches, plans, contact. Interactive reference on the docs page.
Who This Is For
- Match trackers — recent results + "what's next today"
- Discord / Telegram bots —
/resultsand/schedulewithout two scrapers - Widgets and landing pages — embed upcoming fixtures for a team or event
- Analytics side projects — stable IDs across played history
If you only need one Discord command for last results, see the dedicated Discord bot guide — this article is the broader played + upcoming picture.
Authentication
Every request sends your key in the header:
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.ggscore.net/v1/played-matches?limit=5"
Get keys from the site login / cabinet. Rotate keys if leaked; treat them like SMTP passwords.
Common responses:
- 401 — missing or invalid key
- 429 — over quota; back off, cache, upgrade tier
- 200 — JSON body; shape per docs
Played Matches: Results You Can Show
Use when: final scores, map counts, winner/loser, event name, played-at timestamp.
Typical flow:
GETplayed matches (filters per docs: limit, team, event, date range).- Map each row to UI: team names, score string (
2:1), event, online/offline flag. - Cache 30–120 seconds on hot paths; played data changes slowly after match end.
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.ggscore.net/v1/played-matches?limit=10"
const BASE = 'https://api.ggscore.net/v1'; // verify in docs
async function fetchPlayed(limit = 10) {
const res = await fetch(`${BASE}/played-matches?limit=${limit}`, {
headers: { 'X-API-Key': process.env.GGSCORE_API_KEY },
});
if (res.status === 429) throw new Error('Rate limited');
if (!res.ok) throw new Error(`API ${res.status}`);
return res.json();
}
Field names (winner, score, played_at, etc.) — copy from live docs, not from this snippet.
Upcoming Matches: Fixtures Before They Start
Use when: schedule views, reminders, "matches today", tournament calendars.
Typical flow:
GETupcoming matches (filters per docs).- Show start time, teams, event, location/online.
- Cache 5–15 minutes for schedule UIs; refresh more often only on live tournament days if your plan allows.
curl -s -H "X-API-Key: YOUR_API_KEY" \
"https://api.ggscore.net/v1/upcoming-matches?limit=10"
import os
import requests
BASE = "https://api.ggscore.net/v1" # verify in docs
def fetch_upcoming(limit=10):
r = requests.get(
f"{BASE}/upcoming-matches",
params={"limit": limit},
headers={"X-API-Key": os.environ["GGSCORE_API_KEY"]},
timeout=15,
)
r.raise_for_status()
return r.json()
Design tip: one screen that tabs Results (played) and Schedule (upcoming) should use two endpoints, not one mega-poll — easier caching and clearer 429 budgeting.
Plans: Free to Prototype, Paid for Production
Copy numbers from the live pricing page when you ship. As of the public site structure:
| Tier | Orient. quota | Typical use |
|---|---|---|
| Free | Low daily requests (e.g. 3/day) | Key test, local dev, one cron |
| Premium | Higher daily cap (e.g. 100/day) | Small bot or widget with cache |
| Black | Unlimited-scale positioning | Production traffic, priority support |
Free is not a production plan. It is enough to validate JSON shape and your mapping code. When real users poll played + upcoming on a schedule, budget requests:
- played refresh: every 1–2 min on live day (if tier allows)
- upcoming refresh: every 10–30 min
- never call the API on every page view without server-side cache
Caching and Rate Limits (Non-Negotiable)
- Server-side cache — Redis, in-memory, or CDN edge; clients hit your backend, not GGScore on every click.
- Single-flight — dedupe concurrent identical requests during cache miss.
- 429 handling — exponential backoff; surface "try again" in bots, not silent failure.
- ETag / If-None-Match — only if docs support it; otherwise TTL cache.
With 3 requests/day on Free, you cannot run a public tracker. That is expected — upgrade when you measure need.
Combining Played + Upcoming in One App
Minimal architecture:
[Your app]
├─ cache:played ← GET played-matches (TTL 60–120s)
└─ cache:upcoming ← GET upcoming-matches (TTL 300–900s)
- Landing page: upcoming today + last 5 played — 2 API calls per cache window, not per visitor.
- Bot: subcommands map 1:1 to endpoints; hash last payload to skip useless Discord edits.
- Cron: nightly job refreshes upcoming for the week; played refreshed after known match end windows.
Countries endpoint (per docs) helps filters and flags without scraping Wikipedia.
When Scraping Still Tempts You
Niche stats (player ratings, HLTV-only columns) may sit outside the API. Keep scrapers optional and off the critical path — core loop stays on GGScore for played/upcoming integrity.
Checklist Before Go-Live
- Paths and query params match current docs
- API key in env, not in client-side JS
- Cache layer with TTLs per endpoint
- 429 and 401 logged with alert
- Tier matches measured daily request count (played + upcoming + retries)
- UI labels: data via API, not affiliated with Valve / tournament organizers unless licensed
CTA
- Get an API key at ggscore.net — start on Free, measure traffic, upgrade when needed.
- Read the docs for ReDoc reference, TypeScript types, and exact response schemas.
- Build the weekend project on played + upcoming; move to Premium or Black when users depend on it.
Bottom Line
GGScore sells structured CS2 match data — played results and upcoming fixtures — so you spend complexity on product, not parsers. Treat it as a paid API: prototype free, cache aggressively, scale with the plan that matches your poll pattern.