guide
"CS2 API quickstart: your first call in five minutes"
Jul 10, 2026
Bottom line: You can go from zero to a live CS2 match feed in about five minutes. Sign up, put your key in an
Authorization: Bearerheader, and callGET /v1/cs2/matches. Every response is a JSON envelope — adataarray or object plus ametablock — and the one thing that trips people up first is that list rows and detail rows return different shapes. This guide covers all of it, with runnable code in three languages, against the EsportsOdds CS2 data API.
What you'll build
By the end of this page you'll have made an authenticated request, pulled the list of Counter-Strike 2 matches, and read the fields out of the response. That's the foundation every other guide here builds on, from a live-scores dashboard to a Discord score bot.
Step 1: Get an API key
Create an account on the dashboard and generate a key. Keys look like eo_live_… and are shown once, so store yours somewhere safe. Every example below reads it from an environment variable so you never paste a secret into code:
export ESPORTSODDS_API_KEY="eo_live_your_key_here"
There's a single plan — $99/month for 10,000 requests — and no separate keys to juggle for different endpoints. One key reaches the whole API.
Step 2: Make your first request
The base URL is https://api.esportsodds.gg, every path is versioned and namespaced by game, and CS2 lives under /v1/cs2/…. Authenticate by sending your key as a Bearer token:
curl -s https://api.esportsodds.gg/v1/cs2/matches \
-H "Authorization: Bearer $ESPORTSODDS_API_KEY"
If the key is valid you'll get back a block of JSON. If it isn't, you'll get a flat {"error":"…"} with a 401 status — errors are always that simple one-key shape, never a nested structure.
Step 3: Read the envelope
Here's the part worth slowing down on. A list endpoint wraps its results in an envelope: the rows go in data, and a meta object tells you how many came back and whether there's another page.
{
"data": [
{
"id": "0191f2c8-8a1e-7c3a-9f5e-2b6d4e8a1c00",
"status": "completed",
"team_a_name": "Natus Vincere",
"team_b_name": "FaZe Clan",
"score_a": 2,
"score_b": 1,
"tournament_name": "IEM Cologne 2026",
"scheduled_at": "2026-07-08T17:00:00Z"
}
],
"meta": { "count": 1, "next_cursor": null }
}
A single-resource endpoint (like one match by ID) returns {"data": { … }} — the same data key, but an object instead of an array, and no meta. Write your response handling around that split once and every endpoint behaves predictably.
Step 4: The one gotcha: list rows vs detail rows
The matches list you just called is an enriched projection. Each row already carries team_a_name, team_b_name, and tournament_name, so you can render a scoreboard straight from it with no extra lookups.
The single-match detail endpoint is deliberately leaner. GET /v1/cs2/matches/{id} returns a bare match with team_a_id and team_b_id — IDs, not names. If you need the names on a detail view, resolve the IDs against /v1/cs2/teams.
So: reach for the list when you want names, and hydrate IDs from /teams only on detail views. That single distinction saves a surprising number of redundant calls.
The same call in Python and JavaScript
Nothing above is curl-specific. In Python with requests:
import os
import requests
BASE = "https://api.esportsodds.gg"
key = os.environ["ESPORTSODDS_API_KEY"]
resp = requests.get(
f"{BASE}/v1/cs2/matches",
headers={"Authorization": f"Bearer {key}"},
timeout=10,
)
resp.raise_for_status()
payload = resp.json()
for match in payload["data"]:
a, b = match.get("team_a_name"), match.get("team_b_name")
print(f"{a} vs {b} — {match['status']}")
And in JavaScript with fetch:
const BASE = "https://api.esportsodds.gg";
const key = process.env.ESPORTSODDS_API_KEY;
const res = await fetch(`${BASE}/v1/cs2/matches`, {
headers: { Authorization: `Bearer ${key}` },
});
if (!res.ok) throw new Error(`API error: ${res.status}`);
const { data } = await res.json();
for (const m of data) {
console.log(`${m.team_a_name} vs ${m.team_b_name} — ${m.status}`);
}
Both read data the same way, because the envelope is the same everywhere.
Where to go next
You've got a working key and you understand the envelope. From here:
- Pull match data with filters and pagination — narrow the list by status, team, or date, and loop through every page with a cursor.
- Analyse CS2 stats in Python — turn match-stats JSON into a pandas DataFrame.
- Build a resilient client — read the rate-limit headers and handle a
429cleanly before you scale up your call volume.
Start with a match list, confirm the shape, then build outward. The full endpoint reference has every resource you can reach with the key you just made.