Appearance
Catalogue WebSocket
Stream the same changes as Catalogue Events live over a WebSocket.
Download Available Items once, keep its last_update_at, then keep it current from the stream.
Connect
WSS wss://api.skinslink.com/ws/catalogue?games=csgo,rust
Authenticate with your merchant API key — the same one you use for the REST endpoints — in the X-Api-Key header of the WebSocket handshake. Narrow the games with the optional games query parameter; omit it for all.
| Parameter | In | Required | Description |
|---|---|---|---|
X-Api-Key | header | ✅ | Your merchant API key |
games | query | — | Comma-separated game codes (csgo, rust, dota2, tf2). Omit for all |
The endpoint subscribes you on connect — you send nothing. On connect we send one of:
{"event":"auth_success"}— authenticated;catalogue_changeframes start streaming{"event":"auth_failed"}— the API key is missing or invalid; we then close the socket
What we send
Changes arrive in batches — about one per second, and only when something changed. After a slow period several batches may arrive back-to-back. Each batch arrives as:
json
{
"event": "catalogue_change",
"data": {
"since": "2026-09-21T07:42:26.680856848Z",
"next": "2026-09-21T07:42:27.060289140Z",
"count": 2,
"reset": false,
"events": [
{
"type": "upsert",
"at": "2026-09-21T07:42:26.9Z",
"game": "csgo",
"id": "38029384123",
"item": {
"id": "38029384123",
"name": "AK-47 | Redline (Field-Tested)",
"price": 12.45,
"image_url": "https://steamcommunity-a.akamaihd.net/economy/image/class/730/..."
}
},
{ "type": "remove", "at": "2026-09-21T07:42:27.060289140Z", "game": "csgo", "id": "38029111999" }
]
}
}data is the same envelope as a Catalogue Events response — since, next, count, reset, events. Cards always carry the extended fields (class_id, instance_id, asset_id, bot_steam_id, float, paint_seed, inspect_url, phase) when we have them; exterior, rarity and rarity_color are not included. Apply the events as in Applying changes: upsert inserts or replaces the id, remove takes it out. Re-applying an event is safe.
Cursor & catch-up
Every batch carries since and next. Keep the next you last applied, and check each batch:
sinceequals your lastnext— apply the batch, then store itsnext.sincedoes not equal your lastnext— a batch was missed (a slow reader, a brief drop). Pull Catalogue Events from your lastnextuntilmoreisfalse, then resume applying the stream.
On (re)connect
The stream only carries what happens while you are connected. After connecting, first pull Catalogue Events from your stored next to cover the downtime, then apply live batches. Do the same after every reconnect.
Reset
A batch may carry reset: true with no events: download Available Items again and resume from the new last_update_at. A reset always arrives on its own.
Keep-alive & reconnect
We send a WebSocket Ping frame every 30 seconds; your client must answer with Pong within 60 seconds or we close the connection — most libraries do this automatically. There is no text heartbeat message. If the connection drops, reconnect with a growing backoff and catch up with a pull before trusting the live stream again. The connection negotiates permessage-deflate compression automatically when your client offers it.
Example (Python)
python
import json
import websocket # pip install websocket-client
api_key = "your-merchant-api-key"
cursor = catalogue["last_update_at"] # from your downloaded Available Items
def on_open(ws):
catch_up(cursor) # pull from the cursor to cover any downtime, then trust the stream
def on_message(ws, raw):
global cursor
msg = json.loads(raw)
if msg["event"] != "catalogue_change": # auth_success, ...
return
batch = msg["data"]
if batch["reset"]: # download the catalogue again
cursor = download_available_items() # returns the new last_update_at
return
if batch["since"] != cursor: # missed a batch -> pull the gap
catch_up(cursor)
return
apply_events(batch["events"]) # upsert = insert/replace id, remove = delete id
cursor = batch["next"]
def on_close(ws, status, message):
reconnect_with_backoff()
ws = websocket.WebSocketApp(
"wss://api.skinslink.com/ws/catalogue?games=csgo,rust",
header={"X-Api-Key": api_key},
on_open=on_open,
on_message=on_message,
on_close=on_close,
)
ws.run_forever() # answers server pings with pongs automatically