ErrorCS2 API errors

WebSocket disconnects mid-match: reconnect without losing state.

Long-lived sockets drop: proxies close idle connections, phones change networks, servers redeploy. Treat a close as normal. Reconnect with exponential backoff and jitter, resubscribe to your rooms, and use the snapshot the server sends on subscribe to rebuild state.

  • Server ping every 15 s
  • Snapshot on resubscribe
  • SSE fallback on every plan
GET /cs2/matches/live200 OK
$ curl "https://api.citoapi.com/api/v1/cs2/matches/live" \ -H "x-api-key: $CITO_API_KEY"
{  "success": true,  "data": [    {      "id": "cs2-match-2398974",      "eventName": "ROG JOURNEY Autumn 2026",      "bestOf": 3,      "team1Name": "Ninjas in Pyjamas",      "team2Name": "Eternal Fire",      "score": { "team1": 2, "team2": 0 }    },    {      "id": "cs2-match-2398896",      "eventName": "FOX Legacy Season 1",      "bestOf": 3,      "team1Name": "SAW",      "team2Name": "Lazer Cats",      "score": { "team1": 2, "team2": 0 }    }    ...  ]}

The error

What you see
WebSocket connection to 'wss://api.citoapi.com/api/v1/cs2/live/ws' failed
CloseEvent { code: 1006, reason: "", wasClean: false }

What does a mid-match disconnect mean?

The TCP connection under the socket ended. Close code 1006 means it ended without a closing handshake, which is what a network drop, a proxy timeout or a server restart looks like from the client. It says nothing about your subscription being wrong.

Why does it happen?

Most drops come from the path between you and the server, not from the server deciding to close. A few closes are deliberate and should not be retried.

CauseWhat to do
Idle timeout on a proxy or load balancerKeep traffic flowing. The CS2 server sends {"type":"ping"} every 15 seconds; you can also send {"action":"ping"}.
Mobile network change or laptop sleepReconnect with backoff when the socket closes.
Server deployReconnect; the new instance accepts you immediately.
401 on upgrade (MISSING_API_KEY / INVALID_API_KEY)Do not retry. Fix the key.
403 on upgrade (WEBSOCKETS_REQUIRED)Do not retry. CS2 WebSockets need Scale or Enterprise, or the Live WebSockets add-on on Pro; use SSE instead.

How do I reconnect without losing match state?

On every open, subscribe to the match rooms you need. The server answers with subscribed and pushes the current snapshot for each match, so your state is correct again before the next update arrives. Back off exponentially with jitter, and reset the delay after a successful open.

Node.js 22+ or browser: reconnecting CS2 live socket
const KEY = process.env.CITO_API_KEY
const ROOMS = ["match:cs2-match-2398722"]          // or ["all"]
let delay = 1000

function connect() {
  const ws = new WebSocket(`wss://api.citoapi.com/api/v1/cs2/live/ws?apiKey=${KEY}`)
  let lastMessage = Date.now()

  ws.onopen = () => {
    delay = 1000
    ws.send(JSON.stringify({ action: "subscribe", rooms: ROOMS }))
  }
  ws.onmessage = (ev) => {
    lastMessage = Date.now()
    const msg = JSON.parse(ev.data)
    if (msg.type === "ping" || msg.type === "pong") return
    if (msg.type === "error") return console.error(msg.code, msg.message)
    render(msg) // the snapshot after subscribe, then every update
  }
  // No message for 45 s (three missed pings) means a dead connection.
  const watchdog = setInterval(() => {
    if (Date.now() - lastMessage > 45_000) ws.close()
  }, 5_000)

  ws.onclose = () => {
    clearInterval(watchdog)
    const wait = delay + Math.random() * 1000
    delay = Math.min(delay * 2, 30_000)
    setTimeout(connect, wait)
  }
}

function render(msg) { console.log(msg) }
connect()

Is there a fallback when WebSockets are blocked?

Yes. The Server-Sent Events stream at /api/v1/cs2/live/stream works on every plan over plain HTTPS, which corporate proxies rarely block. It counts one request at connect and one per 15 seconds open, and EventSource reconnects on its own.

SSE from a server (Node.js, eventsource package)
import { EventSource } from "eventsource"

const es = new EventSource("https://api.citoapi.com/api/v1/cs2/live/stream?matchId=cs2-match-2398722", {
  fetch: (url, init) => fetch(url, { ...init, headers: { ...init.headers, "x-api-key": process.env.CITO_API_KEY } }),
})
es.onmessage = (ev) => console.log(JSON.parse(ev.data))

How does the CS2 API make a drop harmless?

The server pushes the full current snapshot of every match you resubscribe to, so a reconnect can never leave a stale scoreboard. Close reasons that will not fix themselves (401 on a bad key, 403 on a plan without WebSockets) come back as explicit codes on the upgrade, and SSE and webhooks are there when sockets are not an option.

Frequently asked questions

Something missing? Email support@citoapi.com.

Why does my WebSocket close with code 1006?

1006 means the connection ended without a closing handshake: a network drop, a proxy timeout or a server restart. Reconnect with backoff; it is not an error in your request.

How often does the CS2 live WebSocket send a heartbeat?

Every 15 seconds the server sends {"type":"ping"}. Clients can send {"action":"ping"} at any time and get {"type":"pong"} back.

Do I lose updates while reconnecting?

You can miss individual updates, but not state: after you resubscribe the server pushes the current snapshot of each match, so the scoreboard is correct again.

Which plans include CS2 WebSockets?

Scale and Enterprise include them; Pro can add them with the Live WebSockets add-on. The SSE stream is available on every plan.

See pricing

Build CS2 live score apps

Free is for building and testing. Paid plans add commercial use, real-time live data and the full archive.