Full coverage of players, rooms, matchmaking, leaderboards and realtime WebSocket rooms — with type hints, dataclass responses and typed errors.
A proper package with type hints, dataclass responses and full coverage of players, rooms, matchmaking and leaderboards.
Native asyncio + websockets client for the realtime relay at wss://realtime.michitai.com.
Works with Pygame, Godot-Python, Panda3D and any backend or tool that can make HTTP requests.
# Requires Python 3.9+ — install the dependencies, then run the example
pip install requests # core SDK
pip install websockets # optional, realtime rooms only
python Game.py
from multiplayer import Client
client = Client("YOUR_API_TOKEN", "YOUR_PRIVATE_TOKEN")
# Register & authenticate a player
reg = client.players.register("PlayerOne", {"level": 1, "class": "mage"})
player_token = reg.private_key
# Create and join a room
room = client.rooms.create(player_token, "Lobby 1", max_players=4)
client.rooms.heartbeat(player_token)
# Fetch the leaderboard
lb = client.leaderboard.get(["level", "score"], limit=10)
for entry in lb.leaderboard:
print(f"#{entry.rank} - {entry.player_name}")
Every response is a dataclass extending ApiResponse —
check response.success,
response.error,
response.error_type (typed enum) and
response.error_message.
Client(
api_token: str,
api_private_token: str,
base_url: str = "https://api.michitai.com/api",
logger: Logger | None = None,
timeout: float = 30.0,
session: requests.Session | None = None,
)
Initializes the SDK with API tokens. Services are exposed as client.players, client.rooms, client.actions, client.updates, client.matchmaking, client.matchmaking_requests, client.leaderboard, client.games and client.time.
client.players.register(name: str, player_data: Any = None) -> PlayerRegisterResponse
Registers a new player with optional custom data. Returns player_id and private_key (the player token).
client.players.authenticate(player_token: str, data_cls: type | None = None) -> PlayerAuthResponse
Authenticates a player using their private token. Pass a dataclass as data_cls to deserialize player.player_data into it.
client.games.get_all_players() -> PlayerListResponse
Retrieves a list of all players (requires private API token).
client.players.heartbeat(player_token: str) -> PlayerHeartbeatResponse
Updates player heartbeat to maintain online status.
client.players.logout(player_token: str) -> PlayerLogoutResponse
Logs out a player and updates their last logout timestamp.
client.players.rename(player_token: str, new_name: str) -> PlayerRenameResponse
Renames a player (2-50 characters).
client.players.ban(player_id: int, ban_duration: BanTime, ban_reason: str | None = None) -> PlayerBanResponse
Bans a player with a BanTime duration (HOUR, DAY, WEEK, MONTH, QUARTER, YEAR, FOREVER) and optional reason. Requires private API token.
client.players.unban(player_id: int) -> PlayerUnbanResponse
Unbans a previously banned player. Requires private API token.
from multiplayer import is_banned
is_banned(response: ApiResponse) -> bool
Checks if an API response indicates the player is banned (error contains "You are banned" or ban_info is present).
client.games.get_data(data_cls: type | None = None) -> GameDataResponse
Retrieves global game data. Pass a dataclass as data_cls for typed data.
client.games.update_data(data: Any) -> SuccessResponse
Updates global game data (requires private API token).
client.players.get_data(player_token: str, data_cls: type | None = None) -> PlayerDataResponse
Retrieves a specific player's data using their authentication token.
client.players.update_data(player_token: str, data: Any) -> SuccessResponse
Updates a specific player's data (level, score, inventory, ...).
client.time.get_server_time() -> ServerTimeResponse
Retrieves current server time: utc (datetime), timestamp and readable.
client.time.get_server_time_with_offset(utc_offset: int) -> ServerTimeWithOffsetResponse
Retrieves server time adjusted by a UTC offset in hours (e.g. 3 for UTC+3, -5 for UTC-5).
client.rooms.create(
player_token: str,
room_name: str,
max_players: int = 4,
password: str | None = None,
host_switch: bool = False,
realtime: bool = False,
player_data: Any = None,
rules: Any = None,
) -> RoomCreateResponse
Creates a new game room with optional password, rules and host player data.
client.rooms.list(search: str | None = None, limit: int | None = None, rules_cls: type | None = None) -> RoomListResponse
Retrieves available game rooms. Pass rules_cls to deserialize each room's rules.
client.rooms.join(player_token: str, room_id: str, password: str | None = None, player_data: Any = None) -> RoomJoinResponse
Joins an existing game room with optional password and player data.
client.rooms.leave(player_token: str) -> RoomLeaveResponse
Leaves the current game room.
client.rooms.get_players(player_token: str, data_cls: type | None = None) -> RoomPlayersResponse
Retrieves all players in the current room with typed player_data.
client.rooms.heartbeat(player_token: str) -> HeartbeatResponse
Sends a heartbeat to maintain connection in the game room.
client.rooms.get_current(player_token: str, rules_cls: type | None = None) -> CurrentRoomResponse
Gets comprehensive room state including pending actions and updates.
client.rooms.stop(player_token: str) -> RoomStopResponse
Stops the current game room (host only). Completely removes the room and all associated data.
client.rooms.kick(player_token: str, player_id: int) -> RoomKickResponse
Kicks a player from the game room (host only). Cannot kick yourself.
client.rooms.update_password(player_token: str, password: str | None = None) -> RoomPasswordResponse
Updates the room password (host only). Pass None to remove it.
client.actions.submit(
player_token: str,
action_type: str,
request_data: Any = None,
target_players: RoomTargetPlayers = RoomTargetPlayers.ALL,
target_players_ids: list[int] | None = None,
) -> ActionSubmitResponse
Submits a game action to target players (HOST, ALL, OTHERS or SPECIFIC ids).
client.actions.poll(player_token: str, data_cls: type | None = None) -> ActionPollResponse
Polls for completed actions targeted at the current player.
client.actions.get_pending(player_token: str, data_cls: type | None = None) -> ActionPendingResponse
Retrieves pending actions to process (host only).
client.actions.complete(
player_token: str,
action_id: str,
status: RoomCompleteActionStatus = RoomCompleteActionStatus.COMPLETED,
response_data: Any = None,
) -> ActionCompleteResponse
Marks an action as complete with optional response data (host only).
client.updates.send(
player_token: str,
type: str,
data: Any = None,
target_players: RoomTargetPlayers = RoomTargetPlayers.ALL,
target_players_ids: list[int] | None = None,
) -> UpdatePlayersResponse
Sends an update to specific players or all players in the room.
client.updates.poll(
player_token: str,
from_players: RoomTargetPlayers = RoomTargetPlayers.HOST,
from_players_ids: list[int] | None = None,
last_update: str | None = None,
data_cls: type | None = None,
) -> PollUpdatesResponse
Polls for updates sent to the current player, optionally filtered by source players and last_update id.
Realtime(realtime_websocket_url: str = "wss://realtime.michitai.com")
Initializes the realtime WebSocket client with optional custom server URL. Requires pip install websockets.
Realtime.get_token(client: Client, player_token: str) -> TokenResponse
Generates a realtime authentication token (player must be in a realtime-enabled room).
await realtime.connect(realtime_token: str) -> bool
Connects to the WebSocket server using the realtime token. Starts the message listener and the 20s heartbeat loop automatically.
await realtime.send(
target: RoomTargetPlayer,
command: str,
data: Any = None,
target_ids: list[int] | None = None,
)
Sends a message to ALL, HOST, OTHERS or SPECIFIC player ids with optional data.
await realtime.disconnect()
Disconnects from the WebSocket server and cleans up tasks.
realtime.on_receive = lambda command, data, sender: ...
realtime.on_connected = lambda: ...
on_receive(command, data, sender) fires for each incoming message; on_connected() fires once the socket is open. Both sync and async callbacks are supported.
client.matchmaking.list(search: str | None = None, limit: int | None = None, rules_cls: type | None = None) -> MatchmakingListResponse
Lists all available matchmaking lobbies with optional search and typed rules.
client.matchmaking.create(
player_token: str,
matchmaking_name: str,
max_players: int = 4,
strict_full: bool = False,
host_switch: bool = False,
can_leave_room: bool = False,
realtime_room: bool = False,
password: str | None = None,
player_data: Any = None,
rules: Any = None,
) -> MatchmakingCreateResponse
Creates a lobby that players can join directly. For approval-based joins use client.matchmaking_requests.create(..., join_by_requests=True).
client.matchmaking_requests.request_to_join(player_token: str, matchmaking_id: str, player_data: Any = None) -> MatchmakingJoinRequestResponse
Requests to join a lobby that requires host approval.
client.matchmaking_requests.respond(player_token: str, request_id: str, action: MatchmakingRequestAction) -> MatchmakingPermissionResponse
Host responds to a join request (MatchmakingRequestAction.APPROVE or REJECT).
client.matchmaking_requests.check_status(player_token: str, request_id: str) -> MatchmakingRequestStatusResponse
Checks the status of a join request.
client.matchmaking.get_current(player_token: str, rules_cls: type | None = None) -> MatchmakingCurrentResponse
Gets the current lobby status including pending join requests.
client.matchmaking.join(player_token: str, matchmaking_id: str, player_data: Any = None) -> MatchmakingDirectJoinResponse
Joins a matchmaking lobby directly (only if it doesn't require approval).
client.matchmaking.leave(player_token: str) -> MatchmakingLeaveResponse
Leaves the current matchmaking lobby.
client.matchmaking.get_players(player_token: str, data_cls: type | None = None) -> MatchmakingPlayersResponse
Gets all players in the current lobby with typed player data.
client.matchmaking.heartbeat(player_token: str) -> MatchmakingHeartbeatResponse
Sends a heartbeat to maintain presence in the lobby.
client.matchmaking.remove(player_token: str) -> MatchmakingRemoveResponse
Removes the matchmaking lobby (host only).
client.matchmaking.start(player_token: str) -> MatchmakingStartResponse
Starts the game: creates a game room and transfers all lobby players (host only).
client.matchmaking.stop(player_token: str) -> MatchmakingStopResponse
Stops the matchmaking lobby (host only, before the game has started).
client.matchmaking.kick(player_token: str, player_id: int) -> MatchmakingKickResponse
Kicks a player from the lobby (host only, before the game has started).
client.matchmaking.update_password(player_token: str, password: str | None = None) -> MatchmakingPasswordResponse
Updates the lobby password (host only, before the game has started). Pass None to remove it.
client.leaderboard.get(sort_by: list[str], limit: int = 10, data_cls: type | None = None) -> LeaderboardResponse
Gets the ranked leaderboard sorted by player-data fields (limit 1-100).
Example Usage:
# Sort by level, then score
response = client.leaderboard.get(["level", "score"], limit=10)
for entry in response.leaderboard:
print(f"#{entry.rank} - {entry.player_name}")
# typed player data: entry.player_data