-
Notifications
You must be signed in to change notification settings - Fork 0
Master Server Queries
steam.game_servers implements the Master Server Query Protocol — Valve's legacy UDP-based server discovery + A2S_INFO / A2S_PLAYER / A2S_RULES requests. Useful for enumerating game servers directly from the master, or interrogating an individual server for player counts, rules, and metadata.
Modern games route this through Steam itself; see the SteamGameServers mixin at steam/client/builtins/gameservers.py for the CM-based path. This page covers the direct UDP protocol.
The whole module is stateless UDP calls — no credentials, no SteamClient, no gevent. Just needs steam installed. No client extra.
from steam import game_servers as gsquery_master(filter_text=r'\nappid\500', max_servers=20, region=MSRegion.World, master=MSServer.Source, timeout=2)
Generator that yields (ip, port) tuples. Filters follow the standard Valve filter grammar (backslash-separated, use raw strings):
for server_addr in gs.query_master(r'\appid\730\white\1', max_servers=3):
print(server_addr)
# ('146.66.152.197', 27073)
# ('146.66.153.124', 27057)
# ('146.66.152.56', 27053)Common filters:
-
\appid\N— only servers running app id N. -
\empty\1— only servers with at least one player. -
\full\1— only servers that are not full. -
\secure\1— only VAC-secured servers. -
\dedicated\1— dedicated only. -
\gamedir\csgo— only servers running the specified mod. -
\map\de_dust2— only servers on a specific map.
Full grammar in steam/game_servers.py's module docstring. Also see Valve's wiki.
Region enum:
gs.MSRegion.US_East / .US_West / .SA / .Europe / .Asia / .Australia
/ .Middle_East / .Africa / .WorldMaster enum:
gs.MSServer.GoldSrc # ('hl1master.steampowered.com', 27010) — shut down
gs.MSServer.Source # ('hl2master.steampowered.com', 27011)
gs.MSServer.Source_27015 # ('208.64.200.65', 27015) — same master, different portWarning: Valve's masters are heavily rate-limited. Big result sets time out silently; there's no way to resume a query. For reliable large-scale enumeration, use SteamClient().gameservers.query(...) from the CM path instead.
Once you have an (ip, port), hit it directly with a2s_*:
Returns a dict with the server's _type ('source' or 'goldsrc'), name, map, folder, game, players, max_players, bots, protocol, environment, vac, app_id, etc.
info = gs.a2s_info(('146.66.152.197', 27073))
print(info['name'], info['map'], f"{info['players']}/{info['max_players']}")force_goldsrc=True — accept only the GoldSrc response format (for old Half-Life 1 era games like Ricochet).
Returns a list of {'index', 'name', 'score', 'duration'} dicts, one per connected player.
players = gs.a2s_players(('146.66.152.197', 27073))
for p in players:
print(f"{p['name']} — score={p['score']} duration={p['duration']:.1f}s")Returns a dict of the server's sv_* cvars — CS-style loadouts, map lists, tick rate, whatever the game has chosen to expose. binary=True returns bytes values instead of decoded strings.
Simple latency probe. Returns a float in milliseconds.
from steam import game_servers as gs
# Find a single TF2 server.
server_addr = next(gs.query_master(r'\appid\40\empty\1\secure\1'))
# Ping it.
print(f"ping: {gs.a2s_ping(server_addr):.1f} ms")
# What is it?
info = gs.a2s_info(server_addr)
print(info['name'], f"map={info['map']}", f"{info['players']}/{info['max_players']}")
# Who's on it?
for p in gs.a2s_players(server_addr):
print(f" {p['name']} — {p['score']} score, {p['duration']:.0f}s")
# What are the rules?
rules = gs.a2s_rules(server_addr)
print('rules:', {k: rules[k] for k in list(rules)[:5]})- Need reliable server discovery for a large game? Use the CM-based path via
SteamClient().gameservers.query(filter_text)— seesteam/client/builtins/gameservers.py. - Building a server browser UI?
a2s_infoon each result gives you everything a real Steam server browser would show.
H47R15/steam — maintained fork of ValvePython/steam. MIT licensed.