Skip to main content

lagclient

PyPI version Python versions CI License: MIT

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 httpx and pydantic.

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 the lag CLI with lag auth login. Sent as Authorization: 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 as Authorization: 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/sdk for TypeScript / Node - sibling package in sdks/node/.
  • The lag CLI in cli/ - also MIT licensed.

License

MIT. See LICENSE.

Metadata

Release files for lagclient 0.2.0

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

Source distribution (sdist)

Source distribution for lagclient 0.2.0
File Size Uploaded
lagclient-0.2.0.tar.gz 31.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lagclient 0.2.0
File Interpreter ABI Platform
lagclient-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 68.3 kB

Release files / lagclient-0.2.0.tar.gz

Download URL lagclient-0.2.0.tar.gz
Size 31.4 kB
Tags Source
SHA-256 checksum
How to use checksums
85a9b82e7eac38372aa170921b0f4bbd0555cf714fe17f7f37b4e21f62bf2ad9
BLAKE2b-256 checksum
How to use checksums
c56119454ab76530d0fa4a8e6cc2787c227fee5eafaed3614ab60cbc4714c8a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.

Transparency log

Release files / lagclient-0.2.0-py3-none-any.whl

Download URL lagclient-0.2.0-py3-none-any.whl
Size 36.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
db33599e51f041918aeb68ac44d7aa7dff74e31d6acc27dd17bb37000d3af8e0
BLAKE2b-256 checksum
How to use checksums
87bfb5daba7e898692229e03de5bce389025ea6ed44e82eeaea984d5420b39a0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

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