Skip to main content

Universal Game API (gameapi)

PyPI version Python 3.9+ License: MIT

One interface. Every game. No API archaeology.

gameapi is a Python library that unifies public game APIs behind a single, consistent interface. Instead of learning a new client for every game, you write the same code for Chess.com, Lichess, and whatever comes next.

PyPI

Go to PyPI and write an email to be invited as a collaborator.

Installation

pip install gameapi
from gameapi import GameAPI

with GameAPI() as api:
    player = api.player(game="chess_com", identifier="hikaru")
    print(player.name, player.rank.rating)   # hikaru 2800

Why

Every game API looks different:

# Without gameapi
chess_client.get_profile(...)
rl_client.fetch_stats(...)
mc_client.player_lookup(...)

gameapi gives you one shape instead:

# With gameapi
api.player(game="chess_com", identifier="...")
api.player(game="lichess", identifier="...")
api.player(game="rocket_league", identifier="...")  # once implemented

Common fields (name, stats, rank) are normalized. Anything that doesn't generalize lives on player.game_data.


Supported Games

Game Slug Auth Required Data Source
Chess.com chess_com No Chess.com Published-Data API
Lichess lichess No Lichess Public API
osu! osu Yes (OAuth) osu! API v2
from gameapi import supported_games
print(supported_games())  # ['chess_com', 'lichess', 'osu']

osu!

osu! is the first integration that requires credentials. Create a free OAuth application at https://osu.ppy.sh/home/account/edit ("New OAuth Application" — no redirect URI needed) and pass the Client ID/Secret as a single api_key string in the form "<client_id>:<client_secret>":

from gameapi import GameAPI

with GameAPI(api_key="12345:your-client-secret") as api:
    player = api.player(game="osu", identifier="mrekk")
    print(player.name, player.rank.rating, player.rank.position)  # mrekk 18000.5 1

gameapi handles the OAuth client-credentials token exchange, caching, and refresh for you — you only ever deal in the two credentials above.

osu! has no head-to-head "match" concept like chess does, so api.matches(game="osu", identifier=...) returns the player's recent play history instead, with result set to "win" for passed plays and "loss" for failed ones. leaderboard(game="osu", region="US") accepts an optional ISO country code to get a country's performance rankings instead of the global one.


Installation

pip install gameapi

Requires Python 3.9+.

Development

git clone https://github.com/F0xyN0xy/universal-game-api.git
cd universal-game-api
pip install -e ".[dev]"
pytest

Quick Start

Player Profile

from gameapi import GameAPI

with GameAPI() as api:
    player = api.player(game="chess_com", identifier="hikaru")
    print(player.name)           # hikaru
    print(player.rank.tier)      # GM
    print(player.rank.rating)    # 2800
    print(player.stats)          # PlayerStats(games_played=..., wins=...)

Recent Matches

for match in api.matches(game="chess_com", identifier="hikaru", limit=10):
    print(match.result, match.opponent, match.played_at)

Leaderboard

board = api.leaderboard(game="chess_com")
for entry in board.top(5):
    print(entry.position, entry.name, entry.rating)

Batch Lookups

players = api.compare_players("chess_com", ["hikaru", "magnuscarlsen", "nihalsarin"])
for p in players:
    print(p.name, p.rank.rating)

Async

import asyncio
from gameapi import AsyncGameAPI

async def main():
    async with AsyncGameAPI() as api:
        player = await api.player(game="lichess", identifier="drnykterstein")
        print(player.name)

asyncio.run(main())

Caching

Optional in-process caching reduces redundant requests:

api = GameAPI(cache=True, cache_ttl=60)  # seconds

Nothing sensitive is ever cached — only parsed response data.


Rate Limits & Retries

gameapi retries transient failures (HTTP 429/500/502/503/504) with exponential backoff, then raises typed exceptions:

from gameapi import RateLimitError

try:
    player = api.player(game="chess_com", identifier="hikaru")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")

429 responses respect the upstream Retry-After header when provided.


Error Handling

All exceptions inherit from GameAPIError:

from gameapi import (
    GameAPIError,
    GameNotSupportedError,
    PlayerNotFoundError,
    AuthenticationError,
    RateLimitError,
    APIUnavailableError,
    InvalidResponseError,
)

Type Safety

gameapi is fully typed and passes mypy --strict with zero errors. All public APIs have complete type annotations using modern Python 3.9+ syntax:

from gameapi import GameAPI, Player

api: GameAPI = GameAPI()
player: Player = api.player(game="chess_com", identifier="hikaru")

# IDE autocomplete works perfectly
rating: float | None = player.rank.rating
games: int | None = player.stats.games_played

Type stubs are included in the package for optimal IDE support.


API Reference

GameAPI(api_key=None, cache=False, cache_ttl=60.0, timeout=10.0, max_retries=2)

Method Returns
player(game, identifier) Player
matches(game, identifier, limit=20) list[Match]
leaderboard(game, region=None) Leaderboard
compare_players(game, identifiers) list[Player]
game_info(game) dict
close() —

Context-manager compatible: with GameAPI() as api:

AsyncGameAPI has identical signatures, await-ed.

Models

  • Player — name, game, identifier, stats, rank, game_data, avatar_url
  • PlayerStats — games_played, wins, losses, draws, win_rate
  • Rank — tier, rating, position, raw
  • Match — id, game, played_at, result, opponent, game_data
  • Leaderboard / LeaderboardEntry — position, name, rating

Every model is a @dataclass, so dataclasses.asdict(player) works out of the box.


Demo Project

A small CLI and dashboard built on gameapi lives in demo-project/:

cd demo-project
pip install -e "."

# Look up a player
python -m demo_project chess_com hikaru -m 5 -l

# Compare two players side-by-side
python src/demo_project/dashboard.py chess_com hikaru magnuscarlsen

Contributing

See CONTRIBUTING.md for how to add a new game integration. The pattern is:

  1. Create src/gameapi/games/<game>/
  2. Subclass GameIntegration
  3. Register it in games/registry.py

No changes to client.py or async_client.py are needed.


License

MIT

Legal

gameapi only integrates with public APIs that permit this kind of access under their terms of service. It does not scrape websites in ways that violate their terms, and does not attempt to bypass authentication, rate limits, or anti-bot protections.

Metadata

Release files for universal-game-api 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for universal-game-api 0.2.1
File Size Uploaded
universal_game_api-0.2.1.tar.gz 28.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for universal-game-api 0.2.1
File Interpreter ABI Platform
universal_game_api-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 57.2 kB

Release files / universal_game_api-0.2.1.tar.gz

Download URL universal_game_api-0.2.1.tar.gz
Size 28.7 kB
Tags Source
SHA-256 checksum
How to use checksums
f8aebb0be38cde228f5495284bb237f6c7bbd545d2e2405a68984d7f81b2ba71
BLAKE2b-256 checksum
How to use checksums
c8fd9b6bf8568f3fa66af1fe4d32594659ebdbde764dca0d00dbaa28b23fd0f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.0

Release files / universal_game_api-0.2.1-py3-none-any.whl

Download URL universal_game_api-0.2.1-py3-none-any.whl
Size 28.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4d8e93bec8f96928b57a409b58d4b679049644220f2f82f556e15c7da3403751
BLAKE2b-256 checksum
How to use checksums
ce689cb73749d6aff0d178625e44aca5a72f60da0238da5560c1568d3fbbe60c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.0

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page