Skip to content
cbyte edited this page Jul 20, 2026 · 2 revisions

WebAPI

steam.webapi.WebAPI is a thin wrapper around Steam's Web API. It hits api.steampowered.com (or partner.steam-api.com), introspects the full interface catalogue at construction time, and lets you call any endpoint as api.Interface.Method(**params).

The wrapper is self-contained — it only needs requests. No gevent, no protobuf, no client extra required.

Do I need a key?

  • Yes — for authenticated endpoints (ISteamUser.*, most user-scoped calls, everything on the Partner host).
  • No — for a handful of read-only endpoints like ISteamWebAPIUtil.GetServerInfo, and for interface discovery on the Public host.

Get a key from steamcommunity.com/dev/apikey. The account needs a purchase history + Steam Guard + verified phone number to be eligible.

Quick start

from steam.webapi import WebAPI

api = WebAPI(key='YOUR_KEY_HERE')

resp = api.ISteamUser.ResolveVanityURL(vanityurl='valve', url_type=2)
# {'response': {'steamid': '103582791429521412', 'success': 1}}

# Or pin a specific version explicitly:
resp = api.ISteamUser.ResolveVanityURL_v1(vanityurl='valve', url_type=2)

# Or fall through to the .call() helper:
resp = api.call('ISteamUser.ResolveVanityURL', vanityurl='valve', url_type=2)

Attribute access is populated at construction time from GetSupportedAPIList — everything Steam exposes to your key becomes a real method with docstring-rendered parameter info:

help(api.ISteamUser.ResolveVanityURL)

Constructor

WebAPI(
    key,
    format='json',
    raw=False,
    https=True,
    http_timeout=30,
    apihost=APIHost.Public,
    auto_load_interfaces=True,
)
  • key — Steam API key. None is accepted for endpoints that don't need one, but interface discovery on the Partner host will fail without a key.
  • format'json', 'vdf', or 'xml'. Default 'json'.
  • raw=True — return the raw response body as a string, skip parsing.
  • https=True — use HTTPS. Required on the Partner host.
  • http_timeout=30 — seconds.
  • apihostAPIHost.Public (api.steampowered.com) or APIHost.Partner (partner.steam-api.com).
  • auto_load_interfaces=True — hit GetSupportedAPIList on construction. Turn off for a bare wrapper you'll populate manually via .load_interfaces(dict).

Every constructor parameter can also be passed per-call to override the default — e.g. api.ISteamUser.ResolveVanityURL(format='vdf', ...).

Module-level get() / post()

For one-off calls without instantiating a WebAPI:

from steam.webapi import get, post

# GET
resp = get('ISteamWebAPIUtil', 'GetServerInfo', 1)

# POST (all params in the request body)
resp = post('ISteamRemoteStorage', 'GetPublishedFileDetails', 1,
            params={
                'key': 'YOUR_KEY_HERE',
                'itemcount': 5,
                'publishedfileids': [1, 1, 1, 1, 1],
            })

Signatures:

get(interface, method, version=1, apihost='api.steampowered.com', https=True, caller=None, session=None, params=None) -> Any

post(interface, method, version=1, apihost='api.steampowered.com', https=True, caller=None, session=None, params=None) -> Any

Under the hood both delegate to webapi_request(url, method, ...) — see steam/webapi.py if you need to hit a URL neither wrapper covers.

Format kwarg

resp = api.ISteamWebAPIUtil.GetServerInfo(format='vdf')

format='json' (default) — response is parsed with json.loads. Returns dict.

format='vdf' — Valve's KeyValues text format. Parsed with the vdf package. Returns dict.

format='xml' — parsed with stdlib xml.etree.ElementTree. Returns an Element. Note: the fork swapped lxml → stdlib xml.etree here. If you need XPath 1.0 or other lxml features, pass raw=True and parse the text yourself.

raw=True — return the raw response text, no parsing.

List parameters

Steam's schema uses param[0] naming for list-shaped inputs. WebAPIMethod handles that transparently:

resp = api.ISteamRemoteStorage.GetPublishedFileDetails(
    itemcount=5,
    publishedfileids=[1, 2, 3, 4, 5],   # serialised as publishedfileids[0]=1, [1]=2, ...
)

Pass a Python list for any parameter Steam declares as an array — the wrapper enforces list at the call site (ValueError on anything else).

Batching — .batch_call()

Many Web API endpoints take a delimited list of IDs in a single parameter — ISteamUser.GetPlayerSummaries accepts up to 100 comma-separated steamids, ISteamUser.GetPlayerBans the same, and so on. Steam enforces per-endpoint caps; the wrapper chunks the input list so you don't have to loop by hand.

batch_call(method_path, id_param, ids, chunk_size=100, join_char=',', **extra_params)
  • method_pathInterface.Method (e.g. 'ISteamUser.GetPlayerSummaries'), same shape .call() takes.
  • id_param — the parameter name on that method that accepts the delimited list (e.g. 'steamids').
  • ids — the full list to batch across.
  • chunk_size=100 — matches GetPlayerSummaries. Consult Steam's docs per endpoint; GetPlayerBans also 100.
  • join_char=',' — most classic I* interfaces want comma-separated. Newer *Service methods often want a JSON array — for those, build the JSON yourself and loop over .call() directly.
  • extra_params — passed verbatim to every chunked call.

Example — 250 steamids across 3 round-trips:

api = WebAPI(key='ABC...')
steamids = [76561198010623137, 76561198010623138, ...]   # 250 items

chunks = api.batch_call(
    'ISteamUser.GetPlayerSummaries',
    id_param='steamids',
    ids=steamids,
    chunk_size=100,
)
# 250 items → chunks of 100 / 100 / 50 = 3 HTTP calls

all_players = [p for chunk in chunks for p in chunk['response']['players']]

Returns a list of per-chunk responses in call order — flatten it as your endpoint's response shape dictates.

JSON-array parameters (newer *Service endpoints)

Endpoints like IStoreBrowseService.GetItems want a JSON-serialised input, not a delimited list. batch_call isn't the right shape for those — build the JSON payload yourself:

import json

CHUNK = 50
for start in range(0, len(app_ids), CHUNK):
    chunk = app_ids[start:start + CHUNK]
    input_json = json.dumps({
        'ids': [{'appid': aid} for aid in chunk],
        'context': {'language': 'english'},
    })
    resp = api.call('IStoreBrowseService.GetItems', input_json=input_json)
    # ...

.doc() / discovery

print(api.doc())                # all interfaces + methods
print(api.ISteamUser.doc())     # single interface
print(api.ISteamUser.ResolveVanityURL.doc())   # single method + params

Or with help():

help(api.ISteamUser.ResolveVanityURL)

Fully populated from Steam's own interface catalog — the docs list every parameter, its type, whether it's required / optional, and any Valve-provided description.

Session sharing

Every WebAPI instance has .session — a requests.Session. Swap it out to share cookies with a WebAuth session:

from steam.webapi import WebAPI
import steam.webauth as wa

user = wa.WebAuth('username')
user.cli_login('password')

api = WebAPI(key='...')
api.session = user.session   # webapi now carries login cookies

API hosts

from steam.webapi import WebAPI, APIHost

api = WebAPI(key='...', apihost=APIHost.Partner)
  • APIHost.Public = api.steampowered.com. HTTP or HTTPS. Some endpoints anonymous, most need a key.
  • APIHost.Partner = partner.steam-api.com. HTTPS only. Every request needs a valid partner key (HTTP 403 otherwise).

Errors

  • ValueError — unknown parameter, missing required parameter, wrong-type list, bad format value.
  • requests.HTTPError — 4xx / 5xx from Steam. webapi_request calls .raise_for_status() before returning.
  • KeyError — malformed response missing an expected key (rare — usually indicates a schema drift).

Where to go next

  • Actual users, not endpoints: SteamID turns any Steam identifier into any other.
  • Auth cookies for the storefront: WebAuth. Then share api.session = webauth.session.
  • Need to browse the raw endpoint catalog by hand? print(api.doc()) after api = WebAPI(key='...').

Clone this wiki locally