Deadlock Research
verifiednote 0010build 66792026-08-16apinetworkmatchesleaderboardecosystem

Programmatic match data and hero leaderboards (official and community APIs)

Question

How do you programmatically obtain (a) match history and per-match stats for a specific player, and (b) "top 100 players per hero" leaderboards for Deadlock — and is any of it an official Valve API?

Summary

Everything network-side below was exercised with real HTTP calls on 2026-08-16, against game build 6679 (which the official IGCVersion_1422450 endpoint confirmed as the live version the same day). Claims taken from external sources rather than calls are labelled reported.

wantusestatus
An official Valve Web API for match dataDoes not exist. Only IGCVersion_1422450 (GC version numbers) is exposed for app 1422450 on api.steampowered.comverified (keyless interface list; keyed list unchecked)
Player match history (per-match stats)GET https://api.deadlock-api.com/v1/players/{account_id}/match-historyverified by call — 5,323 matches, no key needed
Full parsed data for one matchGET https://api.deadlock-api.com/v1/matches/{match_id}/metadataverified by call — ~1 MB JSON, 12 players, items/abilities/damage
Raw match metadata straight from ValveGET .../v1/matches/{match_id}/saltshttp://replay{cluster}.valve.net/1422450/{match_id}_{metadata_salt}.meta.bz2verified by call (HEAD 200 on the Valve replay server)
Region leaderboard (top ~1000)`GET .../v1/leaderboard/{Europe\Asia\NAmerica\SAmerica\Oceania}`verified by call — 1000 entries, names only, no account ids
"Top 100 per hero" from Valve's boardNot available. GET .../v1/leaderboard/{region}/{hero_id} returns the few board players whose top heroes include that hero (9 for Infernus in Europe)verified by call
A real top-N-per-hero rankingGET .../v1/analytics/scoreboards/players?sort_by=...&hero_id=... (deadlock-api's own DB, not a Valve ladder)verified by call
Doing it yourself at the protobuf levelSteam GC via a logged-in bot (SteamKit2 / node-steam-user); messages in SteamDatabase/Protobufs deadlock/messages verified in repo; running a bot not attempted

Scope: this note answers the match-history and leaderboard question. Note 0011 maps the complete API surface — every host in the deadlock-api.com family, all 31 endpoint groups of its OpenAPI spec, licenses, and the rest of the ecosystem — and feeds the site's rendered reference at /api/.

Biggest caveats: the community API is the de-facto standard but explicitly not endorsed by Valve; Valve throttled trackers hard in September 2024 (reported); fresh-from-Steam match history requires the target account to friend one of deadlock-api's bots; and the "player" identifier everywhere is the 32-bit account_id (SteamID3), not SteamID64.

Findings

1. There is no official Deadlock Web API

The keyless Steam Web API interface list contains exactly one interface for app 1422450:

$ curl -s "https://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/"
27 interfaces (keyless view)
citadel/deadlock hits: ['IGCVersion_1422450']

IGCVersion_1422450 serves game-coordinator version numbers and nothing else — and it agrees exactly with the local install (tools/find_game.py → ClientVersion 6679):

$ curl -s "https://api.steampowered.com/IGCVersion_1422450/GetClientVersion/v1/"
{ "result": { "success": true, "min_allowed_version": 6679, "active_version": 6679 } }   HTTP 200

There is no Deadlock equivalent of Dota 2's IDOTA2Match_570. Caveat: the keyless list omits interfaces that require an API key; checking the keyed list needs a registered Steam Web API key (UNVERIFIED — see Open Questions).

Valve's stance. No published rules or primary-source developer statement about third-party API usage were found. What is on the record (reported, secondary sources): around 2024-09-02 Valve applied aggressive rate limits that stopped trackers (tracklock.gg's creator: "Valve turned on even more aggressive rate limits and now there are no new games being tracked" — Dexerto, 2024-09-05); by mid-October 2024 tracking worked again (Inven Global). The current posture is tolerated-but-unendorsed; deadlock-api.com itself carries the disclaimer "deadlock-api.com is not endorsed by Valve…" (verified in its openapi.json info block). Build accordingly: this can be turned off again at any time.

2. The Game Coordinator route (how the data actually flows)

All match data ultimately comes from the Steam Game Coordinator over protobuf, exactly like Dota 2. The messages are tracked in SteamDatabase/Protobufs under deadlock/ (48 files). Verified by downloading citadel_gcmessages_client.proto (55,923 bytes) and reading the definitions:

messagepurposekey fields (verified)
CMsgClientToGCGetMatchHistoryper-player historyaccount_id (uint32), continue_cursor for paging
CMsgClientToGCGetMatchHistoryResponsematches[] with match_id, hero_id, kills/deaths/assists, net_worth, ranked_display_badge…; EResult includes k_eResult_InvalidPermission = 2 and k_eResult_RateLimited = 5
CMsgClientToGCGetMatchMetaDatasalts for one matchmatch_id, returns metadata_salt, replay_salt
CMsgClientToGCGetLeaderboardregion boardleaderboard_region, optional hero_id — hero filtering is native to the GC
CMsgClientToGCGetLeaderboardResponseentries carry only account_name, rank, top_hero_ids — no account id
CMsgClientToGCGetAccountLeaderboardsone account's board placementsaccount_id(region, hero_id, rank) entries

ECitadelLeaderboardRegion (in citadel_gcmessages_common.proto): Europe, Asia, NAmerica, SAmerica, Oceania.

With metadata_salt in hand, the match metadata is a plain unauthenticated HTTP GET from Valve's replay infrastructure — pattern http://replay{cluster_id}.valve.net/1422450/{match_id}_{metadata_salt}.meta.bz2 (bz2-compressed CMsgMatchMetaDataContents protobuf). Verified live:

$ curl -sI "http://replay273.valve.net/1422450/99570153_30254059.meta.bz2"
HTTP/1.1 200 OK
content-type: application/octet-stream
last-modified: Sat, 15 Aug 2026 11:36:28 GMT

Talking to the GC yourself requires a logged-in Steam account session (SteamKit2, node-steam-user + the app's GC channel) — this note did not run a bot; the message shapes above are what a bot would exchange. deadlock-api.com operates such bots and also crowd-sources salts via deadlock-api-ingest, which scans contributors' local Steam HTTP caches for replay URLs (reported).

3. deadlock-api.com — the de-facto community API

Alive and free as of 2026-08-16. Base https://api.deadlock-api.com; interactive docs at /docs, spec at /openapi.json (116 paths; title "Deadlock API", version 0.1.0). The former separate assets host now redirects into it (assets.deadlock-api.com/v2/heroes → 301 → /v1/assets/heroes, verified). Open source: deadlock-api/deadlock-api. No key needed for anything tested below; an optional key (X-API-KEY header or api_key query, per the spec's security schemes) raises the per-endpoint limits and is obtained via their Discord (reported).

Player match history — the core answer to question (a):

$ curl -s "https://api.deadlock-api.com/v1/players/112724001/match-history"
HTTP 200, 3,256,579 bytes, 5,323 matches. Newest entry (abridged):
{ "match_id": 99570153, "hero_id": 60, "player_kills": 4, "player_deaths": 3,
  "player_assists": 21, "match_result": 0, "start_time": 1786791854,
  "match_duration_s": 1770, "net_worth": 32467 }

(Account 112724001 is the Europe leaderboard's rank-1 player — an obviously public example. Fields mirror CMsgClientToGCGetMatchHistoryResponse.Match, plus ranked badge fields.) Per the endpoint's own docs: if the account is friends with one of their Steam bots the response merges a fresh GC fetch with their ClickHouse store; otherwise you get only what ClickHouse already ingested. Rate limits (documented in the spec): 100 req/s per IP for cached reads; bot-friend refreshes 10 req/h per IP (300 req/h with key); force_refetch=true capped at 1 req/h.

Per-match stats:

$ curl -s "https://api.deadlock-api.com/v1/matches/99570153/metadata"
HTTP 200, 1,018,164 bytes. Keys: banned_hero_ids, hero_build_ids, match_info.
match_info.players: 12 entries, each with ability_stats, items, death_details,
assigned_lane, hero_id, kills/deaths/assists, ...

Sourcing tiers with different limits: cache (100 req/s) → their S3 (100 req/10s keyless) → live Steam fetch (3 req/h keyless, 300 req/h with key).

$ curl -s "https://api.deadlock-api.com/v1/matches/99570153/salts"
{"match_id":99570153,"cluster_id":273,"metadata_salt":30254059,"replay_salt":null,
 "metadata_url":"http://replay273.valve.net/1422450/99570153_30254059.meta.bz2","demo_url":null}

Scale (from GET /v1/info, verified): ~61,700 matches fetched/day, 472 M rows of per-player match history, 23.9 M stored match salts.

Finding players. GET /v1/players/steam-search?search_query=... (name search) and GET /v1/players/steam?account_ids=... (profile by id) both worked keyless. The identifier is the 32-bit account_id; conversion verified against Steam itself: 76561198072989729 (steamID64) − 76561197960265728 = 112724001, and steamcommunity.com/id/cantsaymeow/?xml=1 returns that steamID64 with the same persona name the API reported.

4. Leaderboards — and why "top 100 per hero" is not what you think

GET /v1/leaderboard/{region} returns Valve's board (updated hourly per the docs — the GC message relayed verbatim; /raw serves the undecoded CMsgClientToGCGetLeaderboardResponse).

$ curl -s "https://api.deadlock-api.com/v1/leaderboard/Europe"        # 1000 entries
{ "account_name": "hate being sober",
  "possible_account_ids": [112724001, 1557830260, 1645809056],
  "rank": 1, "top_hero_ids": [] }

Observations (all from calls on 2026-08-16):

For an actual "top N players on hero X", the workable route is deadlock-api's analytics over its own match database:

$ curl -s "https://api.deadlock-api.com/v1/analytics/scoreboards/players?sort_by=wins&hero_id=1&min_matches=20&min_unix_timestamp=1752600000&limit=3"
[{"rank":0,"account_id":127308683,"value":916.0,"matches":1626},
 {"rank":1,"account_id":270463672,"value":656.0,"matches":1282},
 {"rank":2,"account_id":46310648,"value":560.0,"matches":1023}]

sort_by supports matches, wins, winrate, kill/death/damage aggregates, etc.; filters include min_average_badge, time windows, and limit (so top-100 is one call). This ranks by observed performance in their DB — it is not a Valve MMR ladder. /v1/players/mmr/{hero_id} exists but its own docs deprecate it: "Valve reports a single account-wide rank, not a per-hero one." Analytics endpoints share a pool of 200 req/min per IP.

5. Privacy gating

6. Other community layers (liveness only)

HTTP probes on 2026-08-16: statlocker.gg 200, tracklock.gg 200, deadlocktracker.gg 200, deadlocklabs.gg 403-to-curl (bot filter; site reported alive). Their data provenance was not audited; deadlocklabs states it uses "the public Deadlock API pipeline" (reported). For building anything, deadlock-api.com is the layer they sit on or parallel to.

Reproduce

python tools/find_game.py    # ClientVersion 6679

# 1. Official surface
curl -s "https://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/" | grep -o '"IGCVersion_1422450"'
curl -s "https://api.steampowered.com/IGCVersion_1422450/GetClientVersion/v1/"

# 2. Protobuf layer
curl -sO "https://raw.githubusercontent.com/SteamDatabase/Protobufs/master/deadlock/citadel_gcmessages_client.proto"
grep -n -E "message CMsg.*(MatchHistory|MatchMeta|Leaderboard)" citadel_gcmessages_client.proto

# 3. Community API — leaderboards
curl -s "https://api.deadlock-api.com/v1/leaderboard/Europe"      | python -c "import json,sys;print(len(json.load(sys.stdin)['entries']))"
curl -s "https://api.deadlock-api.com/v1/leaderboard/Europe/1"    | python -c "import json,sys;print(len(json.load(sys.stdin)['entries']))"

# 4. Community API — player + match (public example: Europe board rank 1)
curl -s "https://api.deadlock-api.com/v1/players/steam?account_ids=112724001"
curl -s "https://api.deadlock-api.com/v1/players/112724001/match-history" | head -c 400
curl -s "https://api.deadlock-api.com/v1/matches/99570153/salts"
curl -sI "http://replay273.valve.net/1422450/99570153_30254059.meta.bz2" | head -2

# 5. Top-N-per-hero (community ranking, not a Valve ladder)
curl -s "https://api.deadlock-api.com/v1/analytics/scoreboards/players?sort_by=wins&hero_id=1&min_matches=20&limit=3"

# 6. Local privacy-setting check (build 6679)
grep -i "MatchHistory_InvalidPermission\|streamer_mode" \
  "C:/Program Files (x86)/Steam/steamapps/common/Deadlock/game/citadel/resource/localization/citadel_main/citadel_main_english.txt"

Match ids, salts, entry counts and the example account's stats will drift — they are live data. The shapes and status codes are the claims.

Gotchas

Open questions

Sources