├── .codespellignore ├── .github ├── dependabot.yaml └── workflows │ └── publish.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .yamllint.yaml ├── LICENCE ├── README.md ├── index.html ├── pyproject.toml └── steam_web_api ├── __init__.py ├── _version.py ├── apps.py ├── client.py ├── constants.py ├── steam.py ├── steam_types.py ├── users.py └── utils.py /.codespellignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deivit24/python-steam-api/4dc584e5e15fe35495c6712911e57ccd6b2a7a8b/.codespellignore -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: publish 2 | on: # yamllint disable-line rule:truthy 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | pypi: 8 | timeout-minutes: 10 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | - name: Setup Python 3.9 14 | uses: actions/setup-python@v5 15 | with: 16 | python-version: '3.9' 17 | architecture: 'x64' 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --progress-bar off --upgrade pip setuptools 21 | python -m pip install --progress-bar off -e .[build] 22 | - name: Build and publish 23 | env: 24 | TWINE_USERNAME: __token__ 25 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 26 | run: | 27 | python -m build 28 | twine check dist/* 29 | twine upload dist/* 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.py[co] 3 | *.egg-info 4 | build 5 | dist 6 | .eggs 7 | .DS_STORE 8 | .coverage 9 | .idea 10 | .tox 11 | .env 12 | 13 | venv/* 14 | *venv 15 | test.py -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/isort 3 | rev: 5.13.2 4 | hooks: 5 | - id: isort 6 | files: steam_web_api 7 | 8 | - repo: https://github.com/astral-sh/ruff-pre-commit 9 | rev: v0.3.5 10 | hooks: 11 | - id: ruff 12 | name: ruff linter 13 | args: [--fix] 14 | files: steam_web_api 15 | - id: ruff-format 16 | name: ruff formatter 17 | files: steam_web_api 18 | 19 | - repo: https://github.com/codespell-project/codespell 20 | rev: v2.2.6 21 | hooks: 22 | - id: codespell 23 | args: [--write-changes] 24 | additional_dependencies: [tomli] 25 | 26 | - repo: https://github.com/pappasam/toml-sort 27 | rev: v0.23.1 28 | hooks: 29 | - id: toml-sort-fix 30 | files: pyproject.toml 31 | 32 | - repo: https://github.com/adrienverge/yamllint 33 | rev: v1.35.1 34 | hooks: 35 | - id: yamllint 36 | args: [--strict, -c, .yamllint.yaml] 37 | files: (.github/|.pre-commit-config.yaml|.yamllint.yaml) 38 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | rules: 4 | line-length: disable 5 | document-start: disable 6 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Fuegleondevelopment.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Get Started 2 | 3 | ## Installation 4 | 5 | `pip install python-steam-api` 6 | 7 | ## Create Steam API web key 8 | 9 | [Steam API Web key](https://steamcommunity.com/dev/apikey) 10 | 11 | Follow instructions to get API Key 12 | 13 | # Set up your environment variable 14 | 15 | To create an environment variable in Python, you can utilize the os module. Use os.environ to access and modify environment variables. To accommodate different operating systems, check the documentation or resources specific to each OS for instructions on setting environment variables. For example, on Windows, you can use the Command Prompt or PowerShell, while on Unix-based systems like Linux or macOS, you can modify configuration files or use terminal commands like export. 16 | 17 | # Basic Usage 18 | 19 | ### Searching for a user 20 | 21 | ```python 22 | import os 23 | from steam_web_api import Steam 24 | 25 | KEY = os.environ.get("STEAM_API_KEY") 26 | steam = Steam(KEY) 27 | 28 | steam.users.search_user("the12thchairman") 29 | ``` 30 | 31 | Response 32 | 33 | ```json 34 | { 35 | "player": { 36 | "steamid": "76561198995017863", 37 | "communityvisibilitystate": 3, 38 | "profilestate": 1, 39 | "personaname": "The12thChairman", 40 | "profileurl": "https://steamcommunity.com/id/the12thchairman/", 41 | "avatar": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6.jpg", 42 | "avatarmedium": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6_medium.jpg", 43 | "avatarfull": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6_full.jpg", 44 | "avatarhash": "427ef7d5f8ad7b21678f69bc8afc95786cf38fe6", 45 | "lastlogoff": 1659923870, 46 | "personastate": 1, 47 | "primaryclanid": "103582791429521408", 48 | "timecreated": 1570311509, 49 | "personastateflags": 0, 50 | "loccountrycode": "US" 51 | } 52 | } 53 | ``` 54 | 55 | ### Getting User details by steam id 56 | 57 | ```python 58 | import os 59 | from steam_web_api import Steam 60 | 61 | KEY = os.environ.get("STEAM_API_KEY") 62 | 63 | steam = Steam(KEY) 64 | 65 | # arguments: steamid 66 | user = steam.users.get_user_details("76561198995017863") 67 | ``` 68 | 69 | Response 70 | 71 | ```json 72 | { 73 | "player": { 74 | "steamid": "76561198995017863", 75 | "communityvisibilitystate": 3, 76 | "profilestate": 1, 77 | "personaname": "The12thChairman", 78 | "profileurl": "https://steamcommunity.com/id/the12thchairman/", 79 | "avatar": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6.jpg", 80 | "avatarmedium": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6_medium.jpg", 81 | "avatarfull": "https://avatars.akamai.steamstatic.com/427ef7d5f8ad7b21678f69bc8afc95786cf38fe6_full.jpg", 82 | "avatarhash": "427ef7d5f8ad7b21678f69bc8afc95786cf38fe6", 83 | "lastlogoff": 1659923870, 84 | "personastate": 1, 85 | "primaryclanid": "103582791429521408", 86 | "timecreated": 1570311509, 87 | "personastateflags": 0, 88 | "loccountrycode": "US" 89 | } 90 | } 91 | ``` 92 | 93 | ### Getting Friends List 94 | 95 | ```python 96 | import os 97 | from steam_web_api import Steam 98 | 99 | KEY = os.environ.get("STEAM_API_KEY") 100 | 101 | 102 | steam = Steam(KEY) 103 | 104 | # arguments: steamid 105 | user = steam.users.get_user_friends_list("76561198995017863") 106 | ``` 107 | 108 | Response 109 | 110 | ```json 111 | { 112 | "friends": [ 113 | { 114 | "steamid": "76561198164668273", 115 | "communityvisibilitystate": 3, 116 | "profilestate": 1, 117 | "personaname": "ProToType", 118 | "profileurl": "https://steamcommunity.com/id/bruuitssam/", 119 | "avatar": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb.jpg", 120 | "avatarmedium": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_medium.jpg", 121 | "avatarfull": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg", 122 | "avatarhash": "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb", 123 | "lastlogoff": 1659791200, 124 | "personastate": 3, 125 | "realname": "Samuel chance", 126 | "primaryclanid": "103582791429521408", 127 | "timecreated": 1416698360, 128 | "personastateflags": 0, 129 | "loccountrycode": "US", 130 | "relationship": "friend", 131 | "friend_since": 1634692088 132 | }, 133 | { 134 | "steamid": "76561198040366189", 135 | "communityvisibilitystate": 3, 136 | "profilestate": 1, 137 | "personaname": "\u2654 Regular Tetragon", 138 | "commentpermission": 1, 139 | "profileurl": "https://steamcommunity.com/id/regulartetragon/", 140 | "avatar": "https://avatars.akamai.steamstatic.com/85ee384bec86399cc79728cbde046516fa704b23.jpg", 141 | "avatarmedium": "https://avatars.akamai.steamstatic.com/85ee384bec86399cc79728cbde046516fa704b23_medium.jpg", 142 | "avatarfull": "https://avatars.akamai.steamstatic.com/85ee384bec86399cc79728cbde046516fa704b23_full.jpg", 143 | "avatarhash": "85ee384bec86399cc79728cbde046516fa704b23", 144 | "lastlogoff": 1659834670, 145 | "personastate": 0, 146 | "realname": "Vincent Mattingly", 147 | "primaryclanid": "103582791435763797", 148 | "timecreated": 1302294837, 149 | "personastateflags": 0, 150 | "relationship": "friend", 151 | "friend_since": 1649989273 152 | }, 153 | { 154 | "steamid": "76561198030124562", 155 | "communityvisibilitystate": 3, 156 | "profilestate": 1, 157 | "personaname": "Robz", 158 | "profileurl": "https://steamcommunity.com/profiles/76561198030124562/", 159 | "avatar": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb.jpg", 160 | "avatarmedium": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_medium.jpg", 161 | "avatarfull": "https://avatars.akamai.steamstatic.com/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg", 162 | "avatarhash": "fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb", 163 | "lastlogoff": 1659320144, 164 | "personastate": 1, 165 | "primaryclanid": "103582791429521408", 166 | "timecreated": 1283739538, 167 | "personastateflags": 0, 168 | "relationship": "friend", 169 | "friend_since": 1634692171 170 | } 171 | ] 172 | } 173 | ``` 174 | 175 | #### Getting Friends List *(flat version)* 176 | 177 | For frequent friend list queries, you can disable enrichment to reduce the number of requests sent to the Steam Web API. 178 | 179 | ```python 180 | import os 181 | from steam_web_api import Steam 182 | 183 | KEY = os.environ.get("STEAM_API_KEY") 184 | 185 | 186 | steam = Steam(KEY) 187 | 188 | # arguments: steamid 189 | user = steam.users.get_user_friends_list("76561198995017863", enriched=False) 190 | ``` 191 | 192 | Response 193 | 194 | ```json 195 | { 196 | "friends": [ 197 | { 198 | "friend_since": 1634692088, 199 | "relationship": "friend", 200 | "steamid": "76561198164668273" 201 | }, 202 | { 203 | "friend_since": 1649989273, 204 | "relationship": "friend", 205 | "steamid": "76561198040366189" 206 | }, 207 | { 208 | "friend_since": 1634692171, 209 | "relationship": "friend", 210 | "steamid": "76561198030124562" 211 | } 212 | ] 213 | } 214 | ``` 215 | 216 | ### Getting Users Recently Played Games 217 | 218 | ```python 219 | import os 220 | from steam_web_api import Steam 221 | 222 | KEY = os.environ.get("STEAM_API_KEY") 223 | 224 | steam = Steam(KEY) 225 | 226 | # arguments: steamid 227 | user = steam.users.get_user_recently_played_games("76561198995017863") 228 | ``` 229 | 230 | ### Getting User Owned Games 231 | 232 | ```python 233 | import os 234 | from steam_web_api import Steam 235 | 236 | KEY = os.environ.get("STEAM_API_KEY") 237 | 238 | steam = Steam(KEY) 239 | 240 | # arguments: steamid 241 | user = steam.users.get_owned_games("76561198995017863") 242 | ``` 243 | 244 | ### Getting User Shared Games / Family Library 245 | 246 | ```python 247 | import os 248 | from steam_web_api import Steam 249 | 250 | KEY = os.environ.get("STEAM_API_KEY") 251 | ACCESS_TOKEN = os.environ.get("STEAM_ACCESS_TOKEN") 252 | 253 | steam = Steam(KEY) 254 | 255 | # arguments: steamid, token, include_owned (default: False) 256 | user = steam.users.get_shared_games("76561198995017863", ACCESS_TOKEN, include_owned=True) 257 | ``` 258 | 259 | ### Getting User Steam Level 260 | 261 | ```python 262 | import os 263 | from steam_web_api import Steam 264 | 265 | KEY = os.environ.get("STEAM_API_KEY") 266 | 267 | steam = Steam(KEY) 268 | 269 | # arguments: steamid 270 | user = steam.users.get_user_steam_level("76561198995017863") 271 | ``` 272 | 273 | ### Getting User Badges 274 | 275 | ```python 276 | import os 277 | from steam_web_api import Steam 278 | 279 | KEY = os.environ.get("STEAM_API_KEY") 280 | 281 | steam = Steam(KEY) 282 | 283 | # arguments: steamid 284 | user = steam.users.get_user_badges("76561198995017863") 285 | ``` 286 | 287 | ### Getting Community Badge Progress 288 | 289 | ```python 290 | import os 291 | from steam_web_api import Steam 292 | 293 | KEY = os.environ.get("STEAM_API_KEY") 294 | 295 | steam = Steam(KEY) 296 | 297 | # arguments: steamid, badgeid 298 | user = steam.users.get_community_badge_progress("", "") 299 | ``` 300 | 301 | ### Getting User Public Account 302 | 303 | ```python 304 | import os 305 | from steam_web_api import Steam 306 | 307 | KEY = os.environ.get("STEAM_API_KEY") 308 | 309 | steam = Steam(KEY) 310 | 311 | # arguments: steamid 312 | user = steam.users.get_account_public_info("") 313 | ``` 314 | 315 | ### Searching for Games 316 | 317 | ```python 318 | import os 319 | from steam_web_api import Steam 320 | 321 | KEY = os.environ.get("STEAM_API_KEY") 322 | 323 | 324 | steam = Steam(KEY) 325 | 326 | # arguments: search 327 | user = steam.apps.search_games("terr") 328 | ``` 329 | 330 | Response 331 | 332 | ```json 333 | { 334 | "apps": [ 335 | { 336 | "id": 105600, 337 | "link": "https://store.steampowered.com/app/105600/Terraria/?snr=1_7_15__13", 338 | "name": "Terraria", 339 | "img": "https://cdn.akamai.steamstatic.com/steam/apps/105600/capsule_sm_120.jpg?t=1590092560", 340 | "price": "$9.99" 341 | }, 342 | { 343 | "id": 1202130, 344 | "link": "https://store.steampowered.com/app/1202130/Starship_Troopers_Terran_Command/?snr=1_7_15__13", 345 | "name": "Starship Troopers: Terran Command", 346 | "img": "https://cdn.akamai.steamstatic.com/steam/apps/1202130/capsule_sm_120.jpg?t=1657104501", 347 | "price": "$29.99" 348 | }, 349 | { 350 | "id": 1176470, 351 | "link": "https://store.steampowered.com/app/1176470/Terra_Invicta/?snr=1_7_15__13", 352 | "name": "Terra Invicta", 353 | "img": "https://cdn.akamai.steamstatic.com/steam/apps/1176470/capsule_sm_120.jpg?t=1659933796", 354 | "price": "" 355 | }, 356 | { 357 | "id": 1945600, 358 | "link": "https://store.steampowered.com/app/1945600/The_Riftbreaker_Metal_Terror/?snr=1_7_15__13", 359 | "name": "The Riftbreaker: Metal Terror", 360 | "img": "https://cdn.akamai.steamstatic.com/steam/apps/1945600/capsule_sm_120.jpg?t=1659109312", 361 | "price": "$9.99" 362 | }, 363 | { 364 | "id": 285920, 365 | "link": "https://store.steampowered.com/app/285920/TerraTech/?snr=1_7_15__13", 366 | "name": "TerraTech", 367 | "img": "https://cdn.akamai.steamstatic.com/steam/apps/285920/capsule_sm_120.jpg?t=1644900341", 368 | "price": "$24.99" 369 | } 370 | ] 371 | } 372 | ``` 373 | 374 | ### App/Game details 375 | 376 | #### Parameters: 377 | 378 | - `app_id` (int): The unique App ID of the app you want to retrieve details for. For example, 105600 corresponds to "Terraria" 379 | i 380 | - `country` (str): An optional parameter representing the ISO Country Code. The default value is "US." 381 | 382 | - `filters` (str): An optional parameter that allows you to specify a list of keys to return in the app details. If not provided, it defaults to "basic." The available filter options include: 383 | 384 | - `basic` (Default): Returns essential information like type, name, steam_appid, required_age, is_free, dlc, detailed_description, short_description, about_the_game, supported_languages, header_image, website, pc_requirements, mac_requirements, and linux_requirements. 385 | 386 | - Optional filters (Specify one or more of these as a comma-separated string): 387 | - controller_support 388 | - dlc 389 | - fullgame 390 | - legal_notice 391 | - developers 392 | - demos 393 | - price_overview 394 | - metacritic 395 | - categories 396 | - genres 397 | - screenshots 398 | - movies 399 | - recommendations 400 | - achievements 401 | Response 402 | 403 | ```python 404 | import os 405 | from steam_web_api import Steam 406 | 407 | KEY = os.environ.get("STEAM_API_KEY") 408 | 409 | terraria_app_id = 105600 410 | steam = Steam(KEY) 411 | 412 | # arguments: app_id 413 | user = steam.apps.get_app_details(terraria_app_id) 414 | 415 | ``` 416 | 417 | ```json 418 | { 419 | "105600": { 420 | "success": true, 421 | "data": { 422 | "type": "game", 423 | "name": "Terraria", 424 | "steam_appid": 105600, 425 | "required_age": 0, 426 | "is_free": false, 427 | "controller_support": "full", 428 | "dlc": [409210, 1323320], 429 | "detailed_description": "Dig, Fight, Explore, Build: The very world is at your fingertips as you fight for survival, fortune, and glory. Will you delve deep into cavernous expanses in search of treasure and raw materials with which to craft ever-evolving gear, machinery, and aesthetics? Perhaps you will choose instead to seek out ever-greater foes to test your mettle in combat? Maybe you will decide to construct your own city to house the host of mysterious allies you may encounter along your travels?

In the World of Terraria, the choice is yours!

Blending elements of classic action games with the freedom of sandbox-style creativity, Terraria is a unique gaming experience where both the journey and the destination are completely in the player’s control. The Terraria adventure is truly as unique as the players themselves!

Are you up for the monumental task of exploring, creating, and defending a world of your own?

\t\t\t\t\t\t\tKey features:
\t\t\t\t\t\t\t
  • Sandbox Play
    \t\t\t\t\t\t\t
  • Randomly generated worlds
    \t\t\t\t\t\t\t
  • Free Content Updates
    \t\t\t\t\t\t\t
", 430 | "about_the_game": "Dig, Fight, Explore, Build: The very world is at your fingertips as you fight for survival, fortune, and glory. Will you delve deep into cavernous expanses in search of treasure and raw materials with which to craft ever-evolving gear, machinery, and aesthetics? Perhaps you will choose instead to seek out ever-greater foes to test your mettle in combat? Maybe you will decide to construct your own city to house the host of mysterious allies you may encounter along your travels?

In the World of Terraria, the choice is yours!

Blending elements of classic action games with the freedom of sandbox-style creativity, Terraria is a unique gaming experience where both the journey and the destination are completely in the player’s control. The Terraria adventure is truly as unique as the players themselves!

Are you up for the monumental task of exploring, creating, and defending a world of your own?

\t\t\t\t\t\t\tKey features:
\t\t\t\t\t\t\t
  • Sandbox Play
    \t\t\t\t\t\t\t
  • Randomly generated worlds
    \t\t\t\t\t\t\t
  • Free Content Updates
    \t\t\t\t\t\t\t
", 431 | "short_description": "Dig, fight, explore, build! Nothing is impossible in this action-packed adventure game. Four Pack also available!", 432 | "supported_languages": "English, French, Italian, German, Spanish - Spain, Polish, Portuguese - Brazil, Russian, Simplified Chinese", 433 | "header_image": "https://cdn.akamai.steamstatic.com/steam/apps/105600/header.jpg?t=1666290860", 434 | "capsule_image": "https://cdn.akamai.steamstatic.com/steam/apps/105600/capsule_231x87.jpg?t=1666290860", 435 | "capsule_imagev5": "https://cdn.akamai.steamstatic.com/steam/apps/105600/capsule_184x69.jpg?t=1666290860", 436 | "website": "http://www.terraria.org/", 437 | "pc_requirements": { 438 | "minimum": "

REQUIRED

  • OS: Windows Xp, Vista, 7, 8/8.1, 10
    \t\t\t\t\t\t\t\t\t\t
  • Processor: 2.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 2.5GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 128mb Video Memory, capable of Shader Model 2.0+
    \t\t\t\t\t\t\t\t\t\t
  • DirectX®: 9.0c or Greater \t
    \t\t\t\t\t\t\t\t\t
", 439 | "recommended": "

RECOMMENDED

  • OS: Windows 7, 8/8.1, 10
    \t\t\t\t\t\t\t\t\t\t
  • Processor: Dual Core 3.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 4GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 256mb Video Memory, capable of Shader Model 2.0+
    \t\t\t\t\t\t\t\t\t\t
  • DirectX®: 9.0c or Greater \t
    \t\t\t\t\t\t\t\t\t
" 440 | }, 441 | "mac_requirements": { 442 | "minimum": "

REQUIRED

  • OS: OSX 10.9.5 - 10.11.6
    \t\t\t\t\t\t\t\t\t\t
  • Processor: 2.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 2.5GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 128mb Video Memory, capable of OpenGL 3.0+ support (2.1 with ARB extensions acceptable
    \t\t\t\t\t\t\t\t\t
", 443 | "recommended": "

RECOMMENDED

  • OS: OSX 10.9.5 - 10.11.6
    \t\t\t\t\t\t\t\t\t\t
  • Processor: Dual Core 3.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 4GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 256mb Video Memory, capable of OpenGL 3.0+ support (2.1 with ARB extensions acceptable
    \t\t\t\t\t\t\t\t\t
" 444 | }, 445 | "linux_requirements": { 446 | "minimum": "

REQUIRED

LINUX
  • OS: Ubuntu 14.04 LTS
    \t\t\t\t\t\t\t\t\t\t
  • Processor: 2.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 2.5GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 128mb Video Memory, capable of OpenGL 3.0+ support (2.1 with ARB extensions acceptable
    \t\t\t\t\t\t\t\t\t
", 447 | "recommended": "

RECOMMENDED

LINUX
  • OS: Ubuntu 14.04 LTS
    \t\t\t\t\t\t\t\t\t\t
  • Processor: Dual Core 3.0 Ghz
    \t\t\t\t\t\t\t\t\t\t
  • Memory: 4GB
    \t\t\t\t\t\t\t\t\t\t
  • Hard Disk Space: 200MB \t
    \t\t\t\t\t\t\t\t\t\t
  • Video Card: 256mb Video Memory, capable of OpenGL 3.0+ support (2.1 with ARB extensions acceptable
    \t\t\t\t\t\t\t\t\t
" 448 | } 449 | } 450 | } 451 | } 452 | ``` 453 | 454 | ### Getting user app stats 455 | 456 | ```python 457 | import os 458 | from steam_web_api import Steam 459 | 460 | KEY = os.environ.get("STEAM_API_KEY") 461 | 462 | steam = Steam(KEY) 463 | 464 | # arguments: steam_id, app_id 465 | user = steam.apps.get_user_stats("", "") 466 | ``` 467 | 468 | ### Getting user app achievements 469 | 470 | ```python 471 | import os 472 | from steam_web_api import Steam 473 | 474 | KEY = os.environ.get("STEAM_API_KEY") 475 | 476 | 477 | steam = Steam(KEY) 478 | 479 | # arguments: steam_id, app_id 480 | user = steam.apps.get_user_achievements("", "") 481 | ``` 482 | 483 | ### Getting user ban status 484 | 485 | ```python 486 | import os 487 | from steam_web_api import Steam 488 | 489 | KEY = os.environ.get("STEAM_API_KEY") 490 | 491 | 492 | steam = Steam(KEY) 493 | 494 | # arguments: steam_id 495 | user = steam.users.get_player_bans("") 496 | ``` 497 | 498 | ```json 499 | { 500 | "players": [ 501 | { 502 | "SteamId": "76561198079362196", 503 | "CommunityBanned": false, 504 | "VACBanned": false, 505 | "NumberOfVACBans": 0, 506 | "DaysSinceLastBan": 0, 507 | "NumberOfGameBans": 0, 508 | "EconomyBan": "none" 509 | } 510 | ] 511 | } 512 | ``` 513 | 514 | ### Retrieves information about a set of published files. 515 | 516 | ```python 517 | import os 518 | from steam_web_api import Steam 519 | 520 | KEY = os.environ.get("STEAM_API_KEY") 521 | 522 | 523 | steam = Steam(KEY) 524 | 525 | key = 526 | # Can be multiple publish file ids 527 | file_ids = [2086515808] 528 | 529 | 530 | publishedfiledetails = steam.apps.file_service_get_details( 531 | key=key, publishedfileids=file_ids, includevotes=True 532 | ) 533 | ``` 534 | 535 | ```python 536 | { 537 | "response": { 538 | "publishedfiledetails": [ 539 | { 540 | "result": 1, 541 | "publishedfileid": "2086515808", 542 | "creator": "76561198021181972", 543 | "creator_appid": 4000, 544 | "consumer_appid": 4000, 545 | "consumer_shortcutid": 0, 546 | "filename": "", 547 | "file_size": "77837190", 548 | "preview_file_size": "542105", 549 | "preview_url": "https://steamuserimages-a.akamaihd.net/ugc/1027328686644128049/6BBC8D5C3F8230DBAA5BACA9F43936E31B3C78D6/", 550 | "url": "", 551 | "hcontent_file": "8253646490380086716", 552 | "hcontent_preview": "1027328686644128049", 553 | "title": "TTT Windmill in the sky", 554 | "short_description": "Updated 18th May 2020 Fragments of land have drifted together to form a new tranquil land, floating way above the cities. While the gardens are peaceful, the wrong step may result in someone disappearing into the clouds. Feedback is greatly appreciated! No", 555 | "time_created": 1588735858, 556 | "time_updated": 1589755683, 557 | "visibility": 0, 558 | "flags": 5632, 559 | "workshop_file": False, 560 | "workshop_accepted": False, 561 | "show_subscribe_all": False, 562 | "num_comments_public": 67, 563 | "banned": False, 564 | "ban_reason": "", 565 | "banner": "76561197960265728", 566 | "can_be_deleted": True, 567 | "app_name": "Garry's Mod", 568 | "file_type": 0, 569 | "can_subscribe": True, 570 | "subscriptions": 19184, 571 | "favorited": 401, 572 | "followers": 0, 573 | "lifetime_subscriptions": 31670, 574 | "lifetime_favorited": 474, 575 | "lifetime_followers": 0, 576 | "lifetime_playtime": "0", 577 | "lifetime_playtime_sessions": "0", 578 | "views": 9468, 579 | "num_children": 0, 580 | "num_reports": 0, 581 | "previews": [ 582 | { 583 | "previewid": "19122426", 584 | "sortorder": 10, 585 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074141/D656F3253B4BDAB6205F5182EFD4AFB2C4989741/", 586 | "size": 396048, 587 | "filename": "20200517235526_1.jpg", 588 | "preview_type": 0, 589 | }, 590 | { 591 | "previewid": "19122427", 592 | "sortorder": 11, 593 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074159/9AAD8E55FAD396A4A0B6B61A45E26B28687EF3EA/", 594 | "size": 478634, 595 | "filename": "20200517235542_1.jpg", 596 | "preview_type": 0, 597 | }, 598 | { 599 | "previewid": "19122428", 600 | "sortorder": 12, 601 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074181/475893AAFEBDC7C44CB40A651B3118D3C0D3C5DE/", 602 | "size": 563097, 603 | "filename": "20200517235624_1.jpg", 604 | "preview_type": 0, 605 | }, 606 | { 607 | "previewid": "19122429", 608 | "sortorder": 13, 609 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074191/4A721CFFA91B53BE05C40E1B80081C1408F46320/", 610 | "size": 296901, 611 | "filename": "20200517235700_1.jpg", 612 | "preview_type": 0, 613 | }, 614 | { 615 | "previewid": "19122430", 616 | "sortorder": 14, 617 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074202/2784BCB0C3F45AE0B487238EC466BB1CD1BD68BD/", 618 | "size": 482422, 619 | "filename": "20200517235705_1.jpg", 620 | "preview_type": 0, 621 | }, 622 | { 623 | "previewid": "19122431", 624 | "sortorder": 15, 625 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074210/0BB495AB795BF0755AE59EB8FE00E32887E839F3/", 626 | "size": 325917, 627 | "filename": "20200517235718_1.jpg", 628 | "preview_type": 0, 629 | }, 630 | { 631 | "previewid": "19122432", 632 | "sortorder": 16, 633 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074222/6A56BB158AA0303E865AAF47E42BE877F329E02A/", 634 | "size": 441783, 635 | "filename": "20200517235728_1.jpg", 636 | "preview_type": 0, 637 | }, 638 | { 639 | "previewid": "19122433", 640 | "sortorder": 17, 641 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074230/8B88EFDCEDD0DEF60EA36F15D78323C311D6AD10/", 642 | "size": 477307, 643 | "filename": "20200517235735_1.jpg", 644 | "preview_type": 0, 645 | }, 646 | { 647 | "previewid": "19122434", 648 | "sortorder": 18, 649 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074240/E0B1D14CD0C43F17FD7B2B6E7AF4BA87D2097A37/", 650 | "size": 442290, 651 | "filename": "20200517235755_1.jpg", 652 | "preview_type": 0, 653 | }, 654 | { 655 | "previewid": "19122435", 656 | "sortorder": 19, 657 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074251/741495F32F584CE23219B7CDF2EC10C7E3B36F58/", 658 | "size": 511552, 659 | "filename": "20200517235817_1.jpg", 660 | "preview_type": 0, 661 | }, 662 | { 663 | "previewid": "19122436", 664 | "sortorder": 20, 665 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074259/4DE7E9061D3BDDC6B7C5CA6995FD3D99B5A665DB/", 666 | "size": 848443, 667 | "filename": "20200517235828_1.jpg", 668 | "preview_type": 0, 669 | }, 670 | { 671 | "previewid": "19122437", 672 | "sortorder": 21, 673 | "url": "https://steamuserimages-a.akamaihd.net/ugc/1027329389695074267/4AC9DBF871C115DC270EF6D9D9A62D5276E4E067/", 674 | "size": 344893, 675 | "filename": "20200517235848_1.jpg", 676 | "preview_type": 0, 677 | }, 678 | { 679 | "previewid": "21893051", 680 | "sortorder": 22, 681 | "youtubevideoid": "2xYzDODU6Ik", 682 | "preview_type": 1, 683 | "external_reference": "", 684 | }, 685 | ], 686 | "tags": [ 687 | {"tag": "Addon", "display_name": "Addon"}, 688 | {"tag": "map", "display_name": "map"}, 689 | {"tag": "Scenic", "display_name": "Scenic"}, 690 | ], 691 | "vote_data": { 692 | "score": 0.7626459002494812, 693 | "votes_up": 146, 694 | "votes_down": 11, 695 | }, 696 | "playtime_stats": {"playtime_seconds": "0", "num_sessions": "0"}, 697 | "language": 0, 698 | "maybe_inappropriate_sex": False, 699 | "maybe_inappropriate_violence": False, 700 | "revision_change_number": "33", 701 | "revision": 1, 702 | "ban_text_check_result": 0, 703 | } 704 | ] 705 | } 706 | } 707 | ``` 708 | 709 | ### Getting user wishlist data 710 | 711 | ```python 712 | import os 713 | from steam_web_api import Steam 714 | 715 | KEY = os.environ.get("STEAM_API_KEY") 716 | 717 | 718 | steam = Steam(KEY) 719 | 720 | # arguments: steam_id, page(0 default) 721 | user = steam.users.get_profile_wishlist("") 722 | ``` 723 | 724 | #### Example response 725 | 726 | ```python 727 | { 728 | "16900": { 729 | "name": "GROUND BRANCH", 730 | "capsule": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/16900/header_292x136.jpg?t=1719067212", 731 | "review_score": 8, 732 | "review_desc": "Very Positive", 733 | "reviews_total": "17,360", 734 | "reviews_percent": 90, 735 | "release_date": "1534253340", 736 | "release_string": "Aug 14, 2018", 737 | "platform_icons": "", 738 | "subs": [ 739 | { 740 | "packageid": 744465, 741 | "bundleid": null, 742 | "discount_block": "
$29.99
", 743 | "discount_pct": null, 744 | "price": "2999" 745 | } 746 | ], 747 | "type": "Game", 748 | "screenshots": [ 749 | "ss_fb28f92b5a4b77ac9b411afc7d2e4441478260e8.jpg", 750 | "ss_4a6538eef15c7a2565fa699531ec8da7c66ddb5d.jpg", 751 | "ss_7b6fe583943afdbc250ddba6e02c31ac886bf559.jpg", 752 | "ss_1a4c8ad287e200d927d56972564ca6deb439c551.jpg" 753 | ], 754 | "review_css": "positive", 755 | "priority": 4, 756 | "added": 1536368868, 757 | "background": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/16900/page_bg_generated_v6b.jpg?t=1719067212", 758 | "rank": 839, 759 | "tags": [ 760 | "Tactical", 761 | "Realistic", 762 | "Military", 763 | "FPS", 764 | "Shooter" 765 | ], 766 | "is_free_game": false, 767 | "deck_compat": "2", 768 | "early_access": 1, 769 | "win": 1 770 | } 771 | } 772 | ``` 773 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
Terraria
18 |
19 | 22 |
23 |
$9.99
Starship Troopers: Terran Command
32 |
33 | 36 |
37 |
$29.99
The Riftbreaker: Metal Terror
45 |
46 | 49 |
50 |
$9.99
Terra Invicta
59 |
60 | 63 |
64 |
TerraTech
73 |
74 | 77 |
78 |
$24.99
80 | 81 | 82 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = 'setuptools.build_meta' 3 | requires = ['setuptools >= 64.0.0'] 4 | 5 | [project] 6 | authors = [{email = 'david.asal@hotmail.com', name = 'David Salazar'}] 7 | classifiers = [ 8 | 'Development Status :: 4 - Beta', 9 | 'Intended Audience :: Developers', 10 | 'License :: OSI Approved :: MIT License', 11 | 'Operating System :: OS Independent', 12 | 'Programming Language :: Python :: 3 :: Only', 13 | 'Programming Language :: Python :: 3.10', 14 | 'Programming Language :: Python :: 3.11', 15 | 'Programming Language :: Python :: 3.12', 16 | 'Programming Language :: Python :: 3.9', 17 | ] 18 | dependencies = ['beautifulsoup4', 'requests'] 19 | description = 'Python Client wrapper for Steam API.' 20 | keywords = ['api', 'python', 'steam community', 'steam', 'steamapi'] 21 | license = {file = 'LICENSE'} 22 | maintainers = [ 23 | {email = 'mathieu.scheltienne@fcbg.ch', name = 'Mathieu Scheltienne'}, 24 | ] 25 | name = 'python-steam-api' 26 | readme = 'README.md' 27 | requires-python = '>=3.9' 28 | version = '2.1.1' 29 | 30 | [project.optional-dependencies] 31 | all = ['python-steam-api[build]', 'python-steam-api[style]'] 32 | build = ['build', 'twine'] 33 | full = ['python-steam-api[all]'] 34 | style = [ 35 | 'codespell[toml]>=2.2.4', 36 | 'isort', 37 | 'ruff>=0.1.8', 38 | 'toml-sort', 39 | 'yamllint', 40 | ] 41 | 42 | [project.urls] 43 | source = 'https://github.com/deivit24/python-steam-api' 44 | tracker = 'https://github.com/deivit24/python-steam-api/issues' 45 | 46 | [tool.codespell] 47 | check-filenames = true 48 | check-hidden = true 49 | ignore-words = '.codespellignore' 50 | skip = 'build,.git,.mypy_cache,.pytest_cache' 51 | 52 | [tool.isort] 53 | extend_skip_glob = [] 54 | line_length = 88 55 | multi_line_output = 3 56 | profile = 'black' 57 | py_version = 39 58 | 59 | [tool.ruff] 60 | extend-exclude = [] 61 | line-length = 120 62 | target-version = 'py39' 63 | 64 | [tool.ruff.format] 65 | docstring-code-format = true 66 | line-ending = "lf" 67 | 68 | [tool.ruff.lint] 69 | ignore = [] 70 | select = ['A', 'B', 'E', 'F', 'UP', 'W'] 71 | 72 | [tool.ruff.lint.per-file-ignores] 73 | '*' = [ 74 | 'B006', # do not use mutable data structures for argument default 75 | 'B904', # 'Within an except clause, raise exceptions with raise ... from ...' 76 | 'UP007', # 'Use `X | Y` for type annotations', requires python 3.10 77 | ] 78 | '__init__.py' = ['F401'] 79 | 80 | [tool.setuptools] 81 | include-package-data = false 82 | 83 | [tool.setuptools.packages.find] 84 | exclude = ['steam_web_api*tests'] 85 | include = ['steam_web_api*'] 86 | 87 | [tool.tomlsort] 88 | all = true 89 | ignore_case = true 90 | trailing_comma_inline_array = true 91 | -------------------------------------------------------------------------------- /steam_web_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .users import Users 2 | from .client import Client 3 | from .apps import Apps 4 | from .steam import Steam 5 | from ._version import __version__ 6 | 7 | __all__ = ["Steam"] 8 | -------------------------------------------------------------------------------- /steam_web_api/_version.py: -------------------------------------------------------------------------------- 1 | """Version number.""" 2 | 3 | # from importlib.metadata import version 4 | 5 | # __version__ = version(__package__) 6 | 7 | 8 | # Currently the __package__ returns steam_web_api which is wrong. That is the 9 | # dir name. Will make updates in the future. Don't know if this actually effects 10 | # the py package 11 | 12 | PACKAGE = "python-steam-api" 13 | 14 | try: 15 | from importlib.metadata import version 16 | 17 | __version__ = version(PACKAGE) 18 | except Exception: 19 | pass 20 | -------------------------------------------------------------------------------- /steam_web_api/apps.py: -------------------------------------------------------------------------------- 1 | import json 2 | import typing 3 | 4 | from bs4 import BeautifulSoup 5 | from requests import Response, request 6 | 7 | from .client import Client 8 | from .constants import API_APP_DETAILS_URL, API_APP_SEARCH_URL 9 | from .utils import buildUrlWithParamsForSearch 10 | 11 | 12 | class Apps: 13 | """Steam Apps API client""" 14 | 15 | def __init__(self, client: Client): 16 | """Constructor for Steam Apps class""" 17 | self.__client = client 18 | self.__search_url = API_APP_SEARCH_URL 19 | self.__app_details_url = API_APP_DETAILS_URL 20 | 21 | def get_app_details(self, app_id: int, country="US", filters: typing.Optional[str] = "basic") -> dict: 22 | """Obtains an apps details 23 | 24 | Args: 25 | app_id (int): App ID. For example 546560 (Half-Life-Alyx) 26 | country (str): ISO Country Code 27 | filters (str): list of keys to return, e.g. "name,platforms,price_overview". If you use multiple appids, 28 | you must set this parameter to "price_overview". 29 | The filter basic returns the following keys: 30 | type 31 | name 32 | steam_appid 33 | required_age 34 | dlc 35 | detailed_description 36 | about_the_game 37 | supported_languages 38 | detailed_description 39 | supported_languages 40 | header_image 41 | website 42 | pc_requirements 43 | mac_requirements 44 | linux_requirements 45 | Any key names except those listed under basic are acceptable as filter values. 46 | Optional filters: 47 | controller_support, 48 | fullgame, 49 | legal_notice, 50 | developers, 51 | demos, 52 | price_overview, 53 | metacritic, 54 | categories, 55 | genres, 56 | screenshots, 57 | movies, 58 | recommendations, 59 | achievements, 60 | """ 61 | response = request( 62 | "get", 63 | self.__app_details_url, 64 | params={"appids": app_id, "cc": country, "filters": filters}, 65 | ) 66 | json_loaded_response = json.loads(response.text) 67 | return json_loaded_response 68 | 69 | def get_user_stats(self, steam_id: int, app_id: int) -> dict: 70 | """Obtains a user's stats for a specific app, includes only completed achievements 71 | along with app specific information 72 | 73 | Args: 74 | steam_id (int): Steam 64 ID 75 | app_id (int): App ID 76 | """ 77 | response = self.__client.request( 78 | "get", 79 | "/ISteamUserStats/GetUserStatsForGame/v2/", 80 | params={"steamid": steam_id, "appid": app_id}, 81 | ) 82 | return response 83 | 84 | def get_user_achievements(self, steam_id: int, app_id: int, language: str = "en") -> dict: 85 | """Obtains information of the user's achievements in the app 86 | 87 | Args: 88 | steam_id (int): Steam 64 ID 89 | app_id (int): App ID 90 | language: (str) Abbriviated language 91 | """ 92 | response = self.__client.request( 93 | "get", 94 | "/ISteamUserStats/GetPlayerAchievements/v1/", 95 | params={"steamid": steam_id, "appid": app_id, "l": language}, 96 | ) 97 | return response 98 | 99 | def file_service_get_details( 100 | self, 101 | key: str, 102 | publishedfileids: list[int], 103 | includetags: bool = True, 104 | includeadditionalpreviews: bool = True, 105 | includechildren: bool = True, 106 | includekvtags: bool = True, 107 | includevotes: bool = True, 108 | short_description: bool = True, 109 | includeforsaledata: bool = True, 110 | includemetadata: bool = True, 111 | language: typing.Optional[int] = None, 112 | return_playtime_stats: int = 30, 113 | appid: typing.Optional[int] = None, 114 | strip_description_bbcode: bool = True, 115 | desired_revision: typing.Optional[int] = None, 116 | includereactions: bool = False, 117 | admin_query: bool = True, 118 | ) -> dict: 119 | """Queries files with the Steamworks Web API 120 | 121 | Args: 122 | key (str): Steamworks Web API user authentication key. 123 | publishedfileids (List[int]): Set of published file IDs to retrieve details for. 124 | includetags (bool): If true, return tag information in the returned details. 125 | includeadditionalpreviews (bool): If true, return preview information in the returned details. 126 | includechildren (bool): If true, return children in the returned details. 127 | includekvtags (bool): If true, return key value tags in the returned details. 128 | includevotes (bool): If true, return vote data in the returned details. 129 | short_description (bool): If true, return a short description instead of the full description. 130 | includeforsaledata (bool): If true, return pricing data, if applicable. 131 | includemetadata (bool): If true, populate the metadata field. 132 | language (Optional[int]): Specifies the localized text to return. Defaults to English. 133 | return_playtime_stats (int): Return playtime stats for the specified number of days before today. 134 | appid (Optional[int]): App ID associated with the published files. 135 | strip_description_bbcode (bool): Strips BBCode from descriptions. 136 | desired_revision (Optional[int]): Return the data for the specified revision. 137 | includereactions (bool): If true, reactions to items will be returned. 138 | admin_query (bool): If true, return hidden items. 139 | """ 140 | params = { 141 | "key": key, 142 | "includetags": includetags, 143 | "includeadditionalpreviews": includeadditionalpreviews, 144 | "includechildren": includechildren, 145 | "includekvtags": includekvtags, 146 | "includevotes": includevotes, 147 | "short_description": short_description, 148 | "includeforsaledata": includeforsaledata, 149 | "includemetadata": includemetadata, 150 | "language": language, 151 | "return_playtime_stats": return_playtime_stats, 152 | "appid": appid, 153 | "strip_description_bbcode": strip_description_bbcode, 154 | "desired_revision": desired_revision, 155 | "includereactions": includereactions, 156 | "admin_query": admin_query, 157 | } 158 | 159 | # Add published file IDs to parameters 160 | for index, file_id in enumerate(publishedfileids): 161 | params[f"publishedfileids[{index}]"] = file_id 162 | 163 | # Remove keys with None values 164 | params = {k: v for k, v in params.items() if v is not None} 165 | 166 | response = self.__client.request("get", "/IPublishedFileService/GetDetails/v1/", params=params) 167 | 168 | return response 169 | 170 | # Is term meant to be any or a string, I'm not familiar enough with steam search so I'll leave it as is 171 | def search_games(self, term, country="US"): 172 | """Searches for games using the information given 173 | Args: 174 | term (Any): Search term 175 | country (str): ISO Country Code 176 | """ 177 | url = self.search_url(term, country) 178 | result = request("get", url) 179 | html = self.__validator(result) 180 | 181 | # Decode the HTML content for Unicode escape sequences 182 | decoded_html = html.encode("utf-8").decode("unicode_escape") 183 | 184 | # Parse the decoded HTML with BeautifulSoup 185 | soup = BeautifulSoup(decoded_html, features="html.parser") 186 | links = soup.find_all("a") 187 | apps = [] 188 | for link in links: 189 | if link.has_attr("data-ds-appid"): 190 | app = {} 191 | string_id = link["data-ds-appid"] 192 | href = link["href"].replace("\\", "").replace('"', "") 193 | app["id"] = [int(i) for i in string_id.replace("\\", "").replace('"', "").split(",")] 194 | app["link"] = href 195 | 196 | # Extracting the div elements and their content 197 | divs = link.find_all("div") 198 | for div in divs: 199 | class_name = div.get("class")[0].strip('\\"') 200 | if class_name == "match_name": 201 | app["name"] = div.text 202 | elif class_name == "match_price": 203 | app["price"] = div.text 204 | elif class_name == "match_img": 205 | img_tag = div.find("img") 206 | if img_tag and img_tag.has_attr("src"): 207 | app["img"] = img_tag["src"].replace("\\", "").replace('"', "") 208 | 209 | apps.append(app) 210 | 211 | return {"apps": apps} 212 | 213 | # This should be a private method imo, I don't know how you would like to name them so I'll leave it as is 214 | # (Maybe change it to all caps since __search_url and __app_details_url are constants?) 215 | def search_url(self, search, country="US"): 216 | params = {"f": "games", "cc": country, "realm": 1, "l": "english"} 217 | result = buildUrlWithParamsForSearch((self.__search_url), search, params=params) 218 | return result 219 | 220 | def __validator(self, result: Response) -> typing.Union[str, dict]: 221 | try: 222 | body = json.dumps(result.text) 223 | except Exception: 224 | body = {} 225 | 226 | if isinstance(body, dict) and body.get("code", None) is not None: 227 | raise Exception(body.get("description")) 228 | elif result.status_code >= 400: 229 | raise Exception(" ".join([str(result.status_code), result.reason])) 230 | elif len(result.text) == 0: 231 | return "OK" 232 | else: 233 | return body 234 | -------------------------------------------------------------------------------- /steam_web_api/client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import typing 3 | 4 | import requests 5 | 6 | from .constants import API_BASE_URL 7 | from .utils import buildUrlWithParams, mergeDict, retry 8 | 9 | 10 | class Client: 11 | """Streams API HTTP client""" 12 | 13 | def __init__(self, key: str, headers: dict = {}): 14 | """Constructor for TypeForm API client""" 15 | self.__headers = mergeDict( 16 | { 17 | "Content-Type": "application/json", 18 | "Accept": "application/json", 19 | }, 20 | headers, 21 | ) 22 | self.key = key 23 | 24 | @retry(times=3, exceptions=(ValueError, TypeError)) 25 | def request(self, method: str, url: str, data: any = {}, params: dict = {}, headers={}) -> typing.Union[str, dict]: 26 | requestUrl = buildUrlWithParams((API_BASE_URL + url), self.key, params) 27 | requestHeaders = mergeDict(self.__headers, headers) 28 | requestData = json.dumps(data) if len(data.keys()) > 0 else "" 29 | result = requests.request(method, requestUrl, data=requestData, headers=requestHeaders) 30 | return self.__validator(result) 31 | 32 | def __validator(self, result: requests.Response) -> typing.Union[str, dict]: 33 | try: 34 | body = json.loads(result.text) 35 | except Exception: 36 | body = {} 37 | 38 | if isinstance(body, dict) and body.get("code", None) is not None: 39 | raise Exception(body.get("description")) 40 | elif result.status_code >= 400: 41 | raise Exception(" ".join([str(result.status_code), result.reason, result.text])) 42 | elif len(result.text) == 0: 43 | return "OK" 44 | else: 45 | return body 46 | -------------------------------------------------------------------------------- /steam_web_api/constants.py: -------------------------------------------------------------------------------- 1 | API_BASE_URL = "https://api.steampowered.com" 2 | API_APP_DETAILS_URL = "https://store.steampowered.com/api/appdetails" 3 | API_APP_SEARCH_URL = "https://store.steampowered.com/search/suggest" 4 | API_APP_WISHLIST = "https://store.steampowered.com/wishlist/profiles" 5 | -------------------------------------------------------------------------------- /steam_web_api/steam.py: -------------------------------------------------------------------------------- 1 | from .users import Users 2 | from .client import Client 3 | from .apps import Apps 4 | 5 | class Steam: 6 | """Steam API client""" 7 | 8 | def __init__(self, key: str, headers: dict = {}): 9 | """Constructor for Steam API client""" 10 | client = Client(key, headers=headers) 11 | self.__users = Users(client) 12 | self.__apps = Apps(client) 13 | 14 | @property 15 | def users(self) -> Users: 16 | return self.__users 17 | 18 | @property 19 | def apps(self) -> Apps: 20 | return self.__apps 21 | -------------------------------------------------------------------------------- /steam_web_api/steam_types.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | 4 | class PublishedFileQueryType(IntEnum): 5 | RANKED_BY_VOTE = 0 6 | RANKED_BY_PUBLICATION_DATE = 1 7 | ACCEPTED_FOR_GAME_RANKED_BY_ACCEPTANCE_DATE = 2 8 | RANKED_BY_TREND = 3 9 | FAVORITED_BY_FRIENDS_RANKED_BY_PUBLICATION_DATE = 4 10 | CREATED_BY_FRIENDS_RANKED_BY_PUBLICATION_DATE = 5 11 | RANKED_BY_NUM_TIMES_REPORTED = 6 12 | CREATED_BY_FOLLOWED_USERS_RANKED_BY_PUBLICATION_DATE = 7 13 | NOT_YET_RATED = 8 14 | RANKED_BY_TOTAL_UNIQUE_SUBSCRIPTIONS = 9 15 | RANKED_BY_TOTAL_VOTES_ASC = 10 16 | RANKED_BY_VOTES_UP = 11 17 | RANKED_BY_TEXT_SEARCH = 12 18 | RANKED_BY_PLAYTIME_TREND = 13 19 | RANKED_BY_TOTAL_PLAYTIME = 14 20 | RANKED_BY_AVERAGE_PLAYTIME_TREND = 15 21 | RANKED_BY_LIFETIME_AVERAGE_PLAYTIME = 16 22 | RANKED_BY_PLAYTIME_SESSIONS_TREND = 17 23 | RANKED_BY_LIFETIME_PLAYTIME_SESSIONS = 18 24 | RANKED_BY_INAPPROPRIATE_CONTENT_RATING = 19 25 | RANKED_BY_BAN_CONTENT_CHECK = 20 26 | RANKED_BY_LAST_UPDATED_DATE = 21 27 | 28 | 29 | class PublishedFileInfoMatchingFileType(IntEnum): 30 | ITEMS = 0 31 | COLLECTIONS = 1 32 | ART = 2 33 | VIDEOS = 3 34 | SCREENSHOTS = 4 35 | COLLECTION_ELIGIBLE = 5 36 | GAMES = 6 37 | SOFTWARE = 7 38 | CONCEPTS = 8 39 | GREENLIGHT_ITEMS = 9 40 | ALL_GUIDES = 10 41 | WEB_GUIDES = 11 42 | INTEGRATED_GUIDES = 12 43 | USABLE_IN_GAME = 13 44 | MERCH = 14 45 | CONTROLLER_BINDINGS = 15 46 | STEAMWORKS_ACCESS_INVITES = 16 47 | ITEMS_MTX = 17 48 | ITEMS_READY_TO_USE = 18 49 | WORKSHOP_SHOWCASE = 19 50 | GAME_MANAGED_ITEMS = 20 51 | -------------------------------------------------------------------------------- /steam_web_api/users.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Union 3 | 4 | from requests import request 5 | 6 | from .client import Client 7 | from .constants import API_APP_WISHLIST 8 | 9 | 10 | class Users: 11 | """Steam Users API client""" 12 | 13 | def __init__(self, client: Client): 14 | """Constructor for Steam Users class""" 15 | self.__client = client 16 | self.__wishlist_url = API_APP_WISHLIST 17 | 18 | def search_user(self, search: str) -> dict: 19 | """Searches for exact match 20 | 21 | Args: 22 | search (str): steam user. For example 'the12thchairman' 23 | """ 24 | search_response = self.__client.request( 25 | "get", "/ISteamUser/ResolveVanityURL/v1/", params={"vanityurl": search} 26 | )["response"] 27 | 28 | if search_response["success"] != 1: 29 | return search_response["message"] 30 | steam_id = search_response["steamid"] 31 | return self.get_user_details(steam_id) 32 | 33 | def get_user_details(self, steam_id: str, single=True) -> dict: 34 | """Gets user/player details by steam ID 35 | 36 | Args: 37 | steam_id (str): Steam 64 ID 38 | single (bool, optional): Gets one player. Defaults to True. When false, steam_id can be a string of steamids 39 | and delimited by a ',' 40 | 41 | """ 42 | if single: 43 | user_response = self.__client.request( 44 | "get", 45 | "/ISteamUser/GetPlayerSummaries/v2/", 46 | params={"steamids": steam_id}, 47 | )["response"] 48 | if "players" in user_response and user_response["players"]: 49 | return {"player": user_response["players"][0]} 50 | else: 51 | return {"player": None} 52 | else: 53 | user_response = {"players": []} 54 | steam_ids = steam_id.split(",") 55 | for i in range(0, len(steam_ids), 100): 56 | batch_ids = steam_ids[i : i + 100] 57 | batch_response = self.__client.request( 58 | "get", 59 | "/ISteamUser/GetPlayerSummaries/v2/", 60 | params={"steamids": ",".join(batch_ids)}, 61 | )["response"] 62 | user_response["players"].extend(batch_response["players"]) 63 | return user_response 64 | 65 | def get_user_friends_list(self, steam_id: str, enriched=True) -> dict: 66 | """Gets friend list of a user 67 | 68 | Args: 69 | steam_id (str): Steam 64 ID 70 | enriched (bool, optional): Enriches the response with user details. Defaults to True. 71 | """ 72 | friends_list_response = self.__client.request( 73 | "get", "/ISteamUser/GetFriendList/v1/", params={"steamid": steam_id} 74 | )["friendslist"] 75 | if enriched: 76 | transform_friends = self._transform_friends(friends_list_response) 77 | return {"friends": transform_friends} 78 | else: 79 | return friends_list_response 80 | 81 | def get_user_recently_played_games(self, steam_id: str) -> dict: 82 | """Gets recently played games 83 | 84 | Args: 85 | steam_id (str): Steam 64 ID 86 | """ 87 | response = self.__client.request( 88 | "get", 89 | "/IPlayerService/GetRecentlyPlayedGames/v1/", 90 | params={"steamid": steam_id}, 91 | )["response"] 92 | return response 93 | 94 | def get_owned_games(self, steam_id: str, include_appinfo=True, includ_free_games=True) -> dict: 95 | """Gets all owned games of a user by steam id 96 | 97 | Args: 98 | steam_id (str): Steam 64 ID 99 | include_appinfo (bool, optional): Includes app/game info. Defaults to True. 100 | includ_free_games (bool, optional): Includes free games. Defaults to True. 101 | """ 102 | params = { 103 | "steamid": steam_id, 104 | "include_appinfo": include_appinfo, 105 | "include_played_free_games": includ_free_games, 106 | } 107 | response = self.__client.request( 108 | "get", 109 | "/IPlayerService/GetOwnedGames/v1/", 110 | params=params, 111 | )["response"] 112 | return response 113 | 114 | def get_user_steam_level(self, steam_id: str) -> dict: 115 | """Gets user steam level 116 | 117 | Args: 118 | steam_id (str): Steam 64 ID 119 | """ 120 | response = self.__client.request( 121 | "get", 122 | "/IPlayerService/GetSteamLevel/v1/", 123 | params={"steamid": steam_id}, 124 | )["response"] 125 | return response 126 | 127 | def get_user_badges(self, steam_id: str) -> dict: 128 | """Gets user steam badges 129 | 130 | Args: 131 | steam_id (str): Steam 64 ID 132 | """ 133 | response = self.__client.request( 134 | "get", 135 | "/IPlayerService/GetBadges/v1/", 136 | params={"steamid": steam_id}, 137 | )["response"] 138 | return response 139 | 140 | def get_community_badge_progress(self, steam_id: str, badge_id: int) -> dict: 141 | """Gets user community badge progress 142 | 143 | Args: 144 | steam_id (str): Steam 64 ID 145 | badge_id (int): Badge ID 146 | """ 147 | response = self.__client.request( 148 | "get", 149 | "/IPlayerService/GetCommunityBadgeProgress/v1", 150 | params={"steamid": steam_id, "badgeid": badge_id}, 151 | )["response"] 152 | return response 153 | 154 | def get_account_public_info(self, steam_id: str) -> dict: 155 | """Gets account public info 156 | 157 | Args: 158 | steam_id (str): Steam 64 ID 159 | """ 160 | response = self.__client.request( 161 | "get", 162 | "/IGameServersService/GetAccountPublicInfo/v1", 163 | params={"steamid": steam_id}, 164 | ) 165 | return response 166 | 167 | def get_player_bans(self, steam_id: str) -> dict: 168 | """Gets account bans info 169 | 170 | Args: 171 | steam_id (str): Steam 64 ID 172 | """ 173 | response = self.__client.request( 174 | "get", 175 | "/ISteamUser/GetPlayerBans/v1", 176 | params={"steamids": steam_id}, 177 | ) 178 | return response 179 | 180 | def _transform_friends(self, friends_list: dict) -> dict: 181 | friend_steam_ids = [friend["steamid"] for friend in friends_list["friends"]] 182 | friends = self.get_user_details(",".join(friend_steam_ids), False)["players"] 183 | for f in friends: 184 | found = next(item for item in friends_list["friends"] if item["steamid"] == f["steamid"]) 185 | f["relationship"] = found["relationship"] 186 | f["friend_since"] = found["friend_since"] 187 | 188 | return friends 189 | 190 | def get_steamid(self, vanity: str) -> dict: 191 | """Get steamid64 from vanity URL 192 | 193 | Args: 194 | vanity (str): Vanity URL 195 | """ 196 | response = self.__client.request( 197 | "get", 198 | "/ISteamUser/ResolveVanityURL/v1", 199 | params={"vanityurl": vanity}, 200 | )["response"] 201 | return response 202 | 203 | def get_profile_wishlist(self, steam_id: str, page: int = 0, version: int = 23) -> Union[dict, list]: 204 | wishlist_profile_url = f"{self.__wishlist_url}/{steam_id}/wishlistdata" 205 | response = request( 206 | "get", 207 | wishlist_profile_url, 208 | params={"p": page, "v": version}, 209 | ) 210 | json_loaded_response = json.loads(response.text) 211 | does_no_exists = json_loaded_response.get("success", None) 212 | # This api is weird. If its successful, it returns a dict with the values 213 | # if its not successful it returns a dict with key called success with the value of 2. 214 | # This means the user does not have a wishwish. This is a, what I have doing if i 215 | # just returning an empty list 216 | if does_no_exists: 217 | return [] 218 | return json_loaded_response 219 | 220 | def get_shared_games(self, steam_id: str, token: str, include_owned: bool = False) -> dict: 221 | """Gets shared games from family library. This is an undocumented API endpoint. 222 | 223 | Args: 224 | steam_id (str): Steam 64 ID 225 | token (str): Steam access token (can be fetched from https://store.steampowered.com/pointssummary/ajaxgetasyncconfig) 226 | """ 227 | # The parameter include_own needs to be set to get all shared games. Apart from its name, it does not mean to get games the user owns. 228 | response = self.__client.request( 229 | "get", 230 | "/IFamilyGroupsService/GetSharedLibraryApps/v1", 231 | params={"steamids": steam_id, "access_token": token, "family_groupid": 0, "include_own": 1, "include_non_games": 0, "include_excluded": 0, "include_free": 0}, 232 | )["response"] 233 | if not include_owned: 234 | response["apps"] = [game for game in response["apps"] if steam_id not in game["owner_steamids"]] 235 | # Filter out family-excluded games like MMO/MP games and delisted games. 236 | # These games are usually not excluded by the include_excluded or include_free parameters (but still unavailable to share) 237 | response["apps"] = [game for game in response["apps"] if game["exclude_reason"] == 0] 238 | return response -------------------------------------------------------------------------------- /steam_web_api/utils.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import urlencode 2 | 3 | 4 | def buildUrlWithParams(url: str, key: str, params={}) -> str: 5 | encoded = urlencode(cleanDict(params)) 6 | return url + "?key=" + key if (len(encoded) == 0) else (url + "?key=" + key + "&" + encoded) 7 | 8 | 9 | def buildUrlWithParamsForSearch(url: str, search: str, params={}) -> str: 10 | encoded = urlencode(cleanDict(params)) 11 | return url + "?term=" + search if (len(encoded) == 0) else (url + "?term=" + search + "&" + encoded) 12 | 13 | 14 | def cleanDict(x: dict = {}) -> dict: 15 | result = {} 16 | for key in x: 17 | if x[key] is not None: 18 | # Check If List 19 | if isinstance(x[key], (list,)): 20 | result[key] = ",".join(x[key]) 21 | # Check If Boolean 22 | elif isinstance(x[key], (bool)): 23 | if x[key] is True: 24 | result[key] = "true" 25 | else: 26 | result[key] = "false" 27 | # Everything Else (Strings/Numbers) 28 | else: 29 | result[key] = x[key] 30 | return result 31 | 32 | 33 | def mergeDict(x: dict, y: dict) -> dict: 34 | z = cleanDict(x) 35 | z.update(cleanDict(y)) 36 | return z 37 | 38 | 39 | def retry(times, exceptions): 40 | """ 41 | Retry Decorator 42 | Retries the wrapped function/method `times` times if the exceptions listed 43 | in ``exceptions`` are thrown 44 | :param times: The number of times to repeat the wrapped function/method 45 | :type times: Int 46 | :param Exceptions: Lists of exceptions that trigger a retry attempt 47 | :type Exceptions: Tuple of Exceptions 48 | """ 49 | 50 | def decorator(func): 51 | def newfn(*args, **kwargs): 52 | attempt = 0 53 | while attempt < times: 54 | try: 55 | return func(*args, **kwargs) 56 | except exceptions: 57 | print("Exception thrown when attempting to run %s, attempt " "%d of %d" % (func, attempt, times)) 58 | attempt += 1 59 | return func(*args, **kwargs) 60 | 61 | return newfn 62 | 63 | return decorator 64 | --------------------------------------------------------------------------------