Official Python SDK for the Lag API
Project description
lagclient
The official Python SDK for the Lag API.
lagclient is a hand-written REST client covering the public Lag API: users,
friends, DMs, servers, rooms, room messages, events, and image uploads. It
does not include the WebSocket protocol or the voice client - those are
out of scope for this package.
Ships both a synchronous Client (backed by httpx.Client) and an
asynchronous AsyncClient (backed by httpx.AsyncClient), sharing a single
Pydantic v2 model layer.
- Typed response models with autocomplete and validation.
- Typed exception hierarchy with automatic retries on transient failures.
- Cursor pagination helpers (sync and async iterators).
- Multipart image upload from path, bytes, or file-like.
- Zero required dependencies beyond
httpxandpydantic.
Install
pip install lagclient
Requires Python 3.9+.
Quickstart
Synchronous
import os
from lagclient import Client
with Client(token=os.environ["LAG_TOKEN"]) as client:
who = client.identity()
print(f"Hello, {who.display_name}")
for server in client.servers.list():
print(f"- {server.name} ({server.member_count} members)")
Asynchronous
import asyncio
import os
from lagclient import AsyncClient
async def main() -> None:
async with AsyncClient(token=os.environ["LAG_TOKEN"]) as client:
who = await client.identity()
print(f"Hello, {who.display_name}")
for server in await client.servers.list():
print(f"- {server.name}")
asyncio.run(main())
Authentication
The SDK accepts two credential types and auto-detects which one you passed:
- A Personal Access Token (
lag_pat_*) - for scripts, CI, and any code acting on behalf of a real user. Create one in the Lag web app under settings, or via thelagCLI withlag auth login. Sent asAuthorization: Bearer <token>. - A Robot API key (
lag_robot_*) - for bots and integrations that act as their own server-scoped identity. Create one when you create a robot on a server. Sent asAuthorization: Robot <key>.
Both types are passed via the same token parameter:
from lagclient import Client
# As a user:
user_client = Client(token="lag_pat_...")
# As a robot:
bot_client = Client(token="lag_robot_abcd1234_...")
The SDK does not implement OAuth flows, refresh tokens, or browser-based login - obtain a token elsewhere and pass it in.
Robots
Robot keys are scoped to a single server and have a fixed permission set.
The SDK switches the auth scheme to Robot automatically and routes
server-scoped actions to the robot endpoints under the hood, so the same
resource methods work for both users and robots:
from lagclient import Client
with Client(token="lag_robot_abcd1234_...") as bot:
me = bot.identity() # GET /robots/@me/info
print(me.display_name, me.permissions)
# Same API as a user, but routed to /robots/@me/servers/...
bot.servers.rooms.messages.send(
me.server_id, "room_id", content="Hello from a robot"
)
page = bot.servers.rooms.messages.list(me.server_id, "room_id")
for msg in page["messages"]:
...
Methods supported with a robot key:
client.identity()client.servers.rooms.list(server_id)client.servers.members.list(server_id)client.servers.rooms.messages.list(server_id, room_id, ...)client.servers.rooms.messages.send(server_id, room_id, ...)client.servers.rooms.messages.edit(server_id, room_id, message_id, ...)client.servers.rooms.messages.delete(server_id, room_id, message_id)
Methods that have no robot equivalent (e.g. users.me(), friends, DMs,
events, image uploads) raise LagInvalidTokenError upfront when called
with a robot key, or return 401/403 from the API.
Configuration
client = Client(
token="lag_pat_...",
base_url="https://api.trylag.com", # default
timeout_seconds=30.0, # default
max_retries=2, # default
user_agent="my-app/1.0", # optional override
extra_headers={"X-My-Header": "v"}, # optional extra headers
# http_client=httpx.Client(...) # bring your own
)
max_retries controls how many times the client retries on a transient
failure (5xx, 429, network error). Backoff is exponential with jitter, capped
at ~8s. The server's Retry-After header is honored on 429.
Both clients are also usable without a context manager:
client = Client(token="lag_pat_...")
try:
client.users.me()
finally:
client.close()
Resources
Every resource hangs off the client instance. The async client has the exact
same tree, just with await in front of each call.
| Attribute | What it covers |
|---|---|
client.system |
/health, /version, /system-status, /config |
client.users |
/users/me, /users/me/avatar, /users/:id, /users/search, Steam helpers |
client.friends |
list, requests, send/accept/decline, remove, block |
client.dms |
conversations + messages with cursor pagination |
client.servers |
servers CRUD, icon upload, leave |
client.servers.invites |
create / list / revoke / preview / join |
client.servers.members |
kick, ban, mute (and lists of active bans/mutes) |
client.servers.roles |
role CRUD and assignment |
client.servers.rooms |
voice rooms |
client.servers.rooms.messages |
room chat: list/send/edit/delete with cursor pagination |
client.events |
server events: list/create/get/update/cancel/RSVP |
client.events.guests |
host-side guest moderation |
client.events.templates |
recurring event templates |
client.images |
multipart upload, metadata, status, delete |
Pagination
DM and room message endpoints return {messages, hasMore, nextCursor}. You
can walk pages yourself:
cursor = None
while True:
page = client.dms.list_messages("c1", limit=50, cursor=cursor)
for msg in page["messages"]:
handle(msg)
if not page["hasMore"] or page["nextCursor"] is None:
break
cursor = page["nextCursor"]
Or use the built-in iterator helpers:
# Sync
for page in client.dms.iter_messages("c1", limit=50):
for msg in page.items:
handle(msg)
# Async
async for page in async_client.dms.iter_messages("c1", limit=50):
for msg in page.items:
handle(msg)
The same pattern works for room messages via
client.servers.rooms.messages.iter(server_id, room_id).
Image upload
# From a file path:
client.images.upload("./avatar.png", purpose="avatar")
# From raw bytes:
with open("./avatar.png", "rb") as f:
client.images.upload(
f.read(),
purpose="avatar",
filename="avatar.png",
content_type="image/png",
)
# From a file-like:
with open("./cover.jpg", "rb") as f:
client.images.upload(f, purpose="event_cover", filename="cover.jpg")
The maximum upload size is 25 MiB.
Error handling
Every non-2xx response becomes a typed exception you can catch precisely:
from lagclient import (
LagAPIError,
LagAuthError,
LagPermissionError,
LagNotFoundError,
LagConflictError,
LagRateLimitError,
LagServerError,
LagConnectionError,
LagInvalidTokenError,
)
try:
client.servers.get("does-not-exist")
except LagNotFoundError:
print("not there")
except LagRateLimitError as err:
print(f"slow down - retry after {err.retry_after_seconds}s")
except LagAPIError as err:
print(f"{err.status}: {err}")
Network failures (DNS, refused, timeouts before any response) are raised as
LagConnectionError. Everything else is a subclass of LagAPIError.
Local development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
mypy src/lagclient
ruff check src tests
python -m build # produces wheel + sdist
Tests use respx to mock httpx transport - no real API is required. Every
resource has its own test file in tests/.
Related
- The Lag API itself -
product/apps/api/in the Lag monorepo. @lag/sdkfor TypeScript / Node - sibling package insdks/node/.- The
lagCLI incli/- also MIT licensed.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lagclient-0.2.0.tar.gz.
File metadata
- Download URL: lagclient-0.2.0.tar.gz
- Upload date:
- Size: 31.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85a9b82e7eac38372aa170921b0f4bbd0555cf714fe17f7f37b4e21f62bf2ad9
|
|
| MD5 |
36b40aa16daa577310f3bddb14e8eeda
|
|
| BLAKE2b-256 |
c56119454ab76530d0fa4a8e6cc2787c227fee5eafaed3614ab60cbc4714c8a8
|
Provenance
The following attestation bundles were made for lagclient-0.2.0.tar.gz:
Publisher:
release.yml on lag-app/sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lagclient-0.2.0.tar.gz -
Subject digest:
85a9b82e7eac38372aa170921b0f4bbd0555cf714fe17f7f37b4e21f62bf2ad9 - Sigstore transparency entry: 1271773366
- Sigstore integration time:
-
Permalink:
lag-app/sdk-python@ad1012ddbfa007956c6d607b28e8dbdd8667c49b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/lag-app
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ad1012ddbfa007956c6d607b28e8dbdd8667c49b -
Trigger Event:
push
-
Statement type:
File details
Details for the file lagclient-0.2.0-py3-none-any.whl.
File metadata
- Download URL: lagclient-0.2.0-py3-none-any.whl
- Upload date:
- Size: 36.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db33599e51f041918aeb68ac44d7aa7dff74e31d6acc27dd17bb37000d3af8e0
|
|
| MD5 |
3c43845efdeeae36d072b37213a55ba5
|
|
| BLAKE2b-256 |
87bfb5daba7e898692229e03de5bce389025ea6ed44e82eeaea984d5420b39a0
|
Provenance
The following attestation bundles were made for lagclient-0.2.0-py3-none-any.whl:
Publisher:
release.yml on lag-app/sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lagclient-0.2.0-py3-none-any.whl -
Subject digest:
db33599e51f041918aeb68ac44d7aa7dff74e31d6acc27dd17bb37000d3af8e0 - Sigstore transparency entry: 1271773391
- Sigstore integration time:
-
Permalink:
lag-app/sdk-python@ad1012ddbfa007956c6d607b28e8dbdd8667c49b -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/lag-app
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ad1012ddbfa007956c6d607b28e8dbdd8667c49b -
Trigger Event:
push
-
Statement type: