Python SDK
There is no official XLADA SDK for Python. The XLADA API is identical to the Remnawave API,
so a working client takes a couple of dozen lines on requests, and the types come from
the OpenAPI specification.
For Remnawave there are community Python libraries built from the same
specification; in theory they apply to XLADA too, but we have not officially
verified them with XLADA. The example below depends only on requests.
Installationâ
python3 -m venv .venv
source .venv/bin/activate
pip install requests
For asynchronous code httpx will do, but for scripts and bots synchronous
requests is enough.
Clientâ
The token is a JWT from POST /api/auth/login or an API token from POST /api/tokens. Both
are passed in the Authorization: Bearer header. The
X-Forwarded-For and X-Forwarded-Proto: https headers are needed when calling the
panel directly: without them the panel's middleware drops the connection.
import os
import requests
PANEL_URL = os.environ.get("XLADA_PANEL_URL", "https://panel.example.com")
API_TOKEN = os.environ.get("XLADA_API_TOKEN", "")
class XladaClient:
def __init__(self, base_url: str = PANEL_URL, token: str = API_TOKEN) -> None:
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update(
{
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"X-Forwarded-For": "127.0.0.1",
"X-Forwarded-Proto": "https",
}
)
def request(self, method: str, path: str, **kwargs) -> dict:
response = self.session.request(method, f"{self.base_url}{path}", timeout=30, **kwargs)
response.raise_for_status()
return response.json()
Authenticationâ
If you have no API token, get an administrator JWT and pass it to the client:
def login(username: str, password: str) -> str:
response = requests.post(
f"{PANEL_URL}/api/auth/login",
json={"username": username, "password": password},
timeout=30,
)
response.raise_for_status()
return response.json()["response"]["accessToken"]
client = XladaClient(token=login("admin", "password"))
A long-lived API token is created by POST /api/tokens â in the UI that is
Settings â API Tokens. The finished token is shown once, in the
response.token field:
created = client.request("POST", "/api/tokens", json={"name": "bot", "expiresInDays": 365})
print(created["response"]["token"])
Listing usersâ
Paginated output: start is the offset, size is the page size (1â1000,
25 by default).
page = client.request("GET", "/api/users", params={"start": 0, "size": 25})
for user in page["response"]["users"]:
print(user["username"], user["status"], user["expireAt"])
print("total:", page["response"]["total"])
Creating a userâ
POST /api/users accepts username (3â36 characters: Latin letters, digits, _, -)
and a required expireAt in ISO 8601 format.
created = client.request(
"POST",
"/api/users",
json={
"username": "john_doe",
"status": "ACTIVE",
"expireAt": "2026-01-17T15:38:45.065Z",
"trafficLimitBytes": 0,
"trafficLimitStrategy": "NO_RESET",
},
)
print(created["response"]["shortUuid"])
Getting a nodeâ
A single node â by uuid; the node list â GET /api/nodes.
node = client.request("GET", f"/api/nodes/{node_uuid}")["response"]
print(node["name"], node["address"], node["port"], node["isConnected"])
To check that the panel is reachable before you start, use
GET /api/auth/status â it requires no authorization.
Do not store the API token in a repository. Take it from an environment variable or
a CI secret, and revoke unneeded tokens via DELETE /api/tokens/{uuid}.
What's nextâ
- TypeScript SDK â a client on
fetchand axios. - Go SDK â a client on the Go standard library.
- API Specification â all 162 endpoints in OpenAPI.
- Webhooks â panel events instead of constant polling.