Multiplayer API Logo

Multiplayer API

Python SDK for
Multiplayer Games

Full coverage of players, rooms, matchmaking, leaderboards and realtime WebSocket rooms — with type hints, dataclass responses and typed errors.

pip Installable

A proper package with type hints, dataclass responses and full coverage of players, rooms, matchmaking and leaderboards.

Async Realtime

Native asyncio + websockets client for the realtime relay at wss://realtime.michitai.com.

Engine Friendly

Works with Pygame, Godot-Python, Panda3D and any backend or tool that can make HTTP requests.

Python SDK

Download

# 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
View on GitHub
Repository

Quick Start

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.

Python API Documentation

Client

Constructor
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.

Player Management — client.players

register
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).

authenticate
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.

get_all_players
client.games.get_all_players() -> PlayerListResponse

Retrieves a list of all players (requires private API token).

heartbeat
client.players.heartbeat(player_token: str) -> PlayerHeartbeatResponse

Updates player heartbeat to maintain online status.

logout
client.players.logout(player_token: str) -> PlayerLogoutResponse

Logs out a player and updates their last logout timestamp.

rename
client.players.rename(player_token: str, new_name: str) -> PlayerRenameResponse

Renames a player (2-50 characters).

ban
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.

unban
client.players.unban(player_id: int) -> PlayerUnbanResponse

Unbans a previously banned player. Requires private API token.

is_banned
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).

Game Data — client.games / client.players

games.get_data
client.games.get_data(data_cls: type | None = None) -> GameDataResponse

Retrieves global game data. Pass a dataclass as data_cls for typed data.

games.update_data
client.games.update_data(data: Any) -> SuccessResponse

Updates global game data (requires private API token).

players.get_data
client.players.get_data(player_token: str, data_cls: type | None = None) -> PlayerDataResponse

Retrieves a specific player's data using their authentication token.

players.update_data
client.players.update_data(player_token: str, data: Any) -> SuccessResponse

Updates a specific player's data (level, score, inventory, ...).

Time Management — client.time

get_server_time
client.time.get_server_time() -> ServerTimeResponse

Retrieves current server time: utc (datetime), timestamp and readable.

get_server_time_with_offset
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).

Room Management — client.rooms

create
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.

list
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.

join
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.

leave
client.rooms.leave(player_token: str) -> RoomLeaveResponse

Leaves the current game room.

get_players
client.rooms.get_players(player_token: str, data_cls: type | None = None) -> RoomPlayersResponse

Retrieves all players in the current room with typed player_data.

heartbeat
client.rooms.heartbeat(player_token: str) -> HeartbeatResponse

Sends a heartbeat to maintain connection in the game room.

get_current
client.rooms.get_current(player_token: str, rules_cls: type | None = None) -> CurrentRoomResponse

Gets comprehensive room state including pending actions and updates.

stop
client.rooms.stop(player_token: str) -> RoomStopResponse

Stops the current game room (host only). Completely removes the room and all associated data.

kick
client.rooms.kick(player_token: str, player_id: int) -> RoomKickResponse

Kicks a player from the game room (host only). Cannot kick yourself.

update_password
client.rooms.update_password(player_token: str, password: str | None = None) -> RoomPasswordResponse

Updates the room password (host only). Pass None to remove it.

Room Actions — client.actions

submit
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).

poll
client.actions.poll(player_token: str, data_cls: type | None = None) -> ActionPollResponse

Polls for completed actions targeted at the current player.

get_pending
client.actions.get_pending(player_token: str, data_cls: type | None = None) -> ActionPendingResponse

Retrieves pending actions to process (host only).

complete
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).

Room Updates — client.updates

send
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.

poll
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 — asyncio + websockets

Constructor
Realtime(realtime_websocket_url: str = "wss://realtime.michitai.com")

Initializes the realtime WebSocket client with optional custom server URL. Requires pip install websockets.

get_token
Realtime.get_token(client: Client, player_token: str) -> TokenResponse

Generates a realtime authentication token (player must be in a realtime-enabled room).

connect
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.

send
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.

disconnect
await realtime.disconnect()

Disconnects from the WebSocket server and cleans up tasks.

Callbacks
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.

Matchmaking — client.matchmaking / client.matchmaking_requests

list
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.

create (direct join)
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).

request_to_join
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.

respond
client.matchmaking_requests.respond(player_token: str, request_id: str, action: MatchmakingRequestAction) -> MatchmakingPermissionResponse

Host responds to a join request (MatchmakingRequestAction.APPROVE or REJECT).

check_status
client.matchmaking_requests.check_status(player_token: str, request_id: str) -> MatchmakingRequestStatusResponse

Checks the status of a join request.

get_current
client.matchmaking.get_current(player_token: str, rules_cls: type | None = None) -> MatchmakingCurrentResponse

Gets the current lobby status including pending join requests.

join
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).

leave
client.matchmaking.leave(player_token: str) -> MatchmakingLeaveResponse

Leaves the current matchmaking lobby.

get_players
client.matchmaking.get_players(player_token: str, data_cls: type | None = None) -> MatchmakingPlayersResponse

Gets all players in the current lobby with typed player data.

heartbeat
client.matchmaking.heartbeat(player_token: str) -> MatchmakingHeartbeatResponse

Sends a heartbeat to maintain presence in the lobby.

remove
client.matchmaking.remove(player_token: str) -> MatchmakingRemoveResponse

Removes the matchmaking lobby (host only).

start
client.matchmaking.start(player_token: str) -> MatchmakingStartResponse

Starts the game: creates a game room and transfers all lobby players (host only).

stop
client.matchmaking.stop(player_token: str) -> MatchmakingStopResponse

Stops the matchmaking lobby (host only, before the game has started).

kick
client.matchmaking.kick(player_token: str, player_id: int) -> MatchmakingKickResponse

Kicks a player from the lobby (host only, before the game has started).

update_password
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.

Leaderboard — client.leaderboard

get
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