Quickstart
From an API key to the next matches, their odds, and a WebSocket stream in four steps.
This walks the full path a typical integration takes: authenticate, list the next matches, pull the
market odds for one, then (optionally) stream updates instead of polling. Every call is a plain
GET (or one POST for the WebSocket ticket) over HTTPS, and every response is the same
{ "data": ..., "meta": ... } envelope with snake_case fields.
Prefer a client library?
Python developers can pip install esportsodds and skip the HTTP plumbing. The package reads
ESPORTSODDS_API_KEY, walks pages for you, retries what is safe to retry and raises a typed
exception for each error code. It uses only the standard library. See the
official Python client.
1. Authenticate
Every request carries your key as a Bearer token. Create a key in the dashboard, then:
export EO_KEY="eo_live_..."
curl -H "Authorization: Bearer $EO_KEY" \
"https://api.esportsodds.gg/v1/cs2/matches?status=completed&limit=1"A 200 with one match in data means the key works: that is the most recent finished match.
See Authentication for the header, the deprecated ?apiKey= fallback, and
how keys are stored.
No subscription yet?
A free sandbox key reads a fixed sample of past matches. The calls on this page ask
for current data, so a sandbox key answers them with 403 (not counted): start from the calls in
the sandbox guide instead, and swap in a paid key later.
2. List the next matches
curl -H "Authorization: Bearer $EO_KEY" \
"https://api.esportsodds.gg/v1/cs2/matches?status=scheduled&sort=scheduled_at&has=odds&limit=5"sort=scheduled_at puts the soonest match first (the default order is newest first, which for
scheduled matches starts with the one furthest away), and has=odds keeps the matches that already
have a market line, so step 3 has something to show.
{
"data": [
{
"id": "018f...e2a1",
"status": "scheduled",
"format": "bo3",
"team_a_id": "018f...aa10",
"team_b_id": "018f...bb20",
"team_a_name": "Natus Vincere",
"team_b_name": "FaZe Clan",
"tournament_name": "IEM Katowice 2026",
"score_a": null,
"score_b": null,
"scheduled_at": "2026-07-01T18:00:00Z"
}
],
"meta": { "count": 1, "next_cursor": "eyJ..." }
}Narrow with status, tournament, team, and date_from/date_to; page with limit + cursor
(pass meta.next_cursor back as ?cursor=). Keep a match id for the next step.
status=live lists the matches being played right now. It returns an empty data when nothing is
on, which is common between about 04:00 and 08:00 UTC, so do not build a first test around it.
3. Get the market odds
curl -H "Authorization: Bearer $EO_KEY" \
"https://api.esportsodds.gg/v1/cs2/odds?match=018f...e2a1&latest=1&market=match_winner"latest=1 returns the current price for each outcome. Leave it off and you get the match's whole
line history instead, a page at a time.
{
"data": [
{ "source": "eo_market", "outcome_key": "home", "label": "Natus Vincere", "price": 1.86, "book_count": 4, "match_id": "018f...e2a1", "captured_at": "2026-07-01T15:05:00Z" },
{ "source": "eo_market", "outcome_key": "away", "label": "FaZe Clan", "price": 1.98, "book_count": 4, "match_id": "018f...e2a1", "captured_at": "2026-07-01T15:05:00Z" }
],
"meta": { "count": 2, "next_cursor": null }
}Each line carries an explicit source: eo_market is the de-vigged multi-book line (with
book_count); eo_model is our modeled line once it clears validation. Add ?source=eo_market to
request just one. Contributing books are never named — see the methodology.
4. Stream updates instead of polling
Once a match is being played, open the WebSocket channel and let updates come to you rather than re-polling steps 2–3 on a timer. It's a two-step ticket handshake so your key never appears in a socket URL:
curl -X POST -H "Authorization: Bearer $EO_KEY" \
"https://api.esportsodds.gg/v1/cs2/ws-token"
# → { "data": { "token": "eyJ...", "expires_at": "..." } }Open the socket, subscribe to the match, and you'll get a full snapshot followed by incremental
update frames. Every frame is a { type, match_id, seq, data } envelope:
const ws = new WebSocket(`wss://api.esportsodds.gg/v1/ws?token=${token}`)
ws.addEventListener('open', () => {
ws.send(JSON.stringify({ type: 'subscribe', data: { match_ids: ['018f...e2a1'] } }))
})
ws.addEventListener('message', (e) => {
const frame = JSON.parse(e.data)
if (frame.type === 'snapshot') setState(frame.match_id, frame.data) // full baseline
if (frame.type === 'update') applyUpdate(frame.match_id, frame.data) // a delta
})The full message types, sequence/reconnect semantics, and connection metering are covered in Live data & WebSocket.
Next steps
- Authentication · Rate limits · Errors
- The full interactive endpoint reference — with a "try it" panel per endpoint — is in the sidebar under API reference.