Skip to main content

x3ui

PyPI Python CI License

Typed Python client for the 3x-ui panel API, generated from OpenAPI.

Unofficial project. Not affiliated with the 3x-ui developers.

Installation

pip install x3ui

Requires Python 3.9+.

Quick start

from x3ui import AuthenticatedClient
from x3ui.api.inbounds import get_panel_api_inbounds_list

client = AuthenticatedClient(base_url="https://panel.example.com:2053", token="YOUR_API_TOKEN")

result = get_panel_api_inbounds_list.sync(client=client)
print(result.success, result.obj)

Base URL must include the port and the panel's base path if you configured one, for example https://panel.example.com:2053/mypath.

Authentication

The panel supports two schemes.

API token (recommended)

Create a token in the panel under Settings → Security → API Token, or through the API, then pass it to AuthenticatedClient. Requests are sent with an Authorization: Bearer <token> header.

from x3ui import AuthenticatedClient

client = AuthenticatedClient(base_url="https://panel.example.com:2053", token="YOUR_API_TOKEN")

Creating a token programmatically:

from x3ui.api.api_tokens import post_panel_api_setting_api_tokens_create
from x3ui.models.post_panel_api_setting_api_tokens_create_body import PostPanelApiSettingApiTokensCreateBody

body = PostPanelApiSettingApiTokensCreateBody(name="automation", scope="admin", expires_at=0)
result = post_panel_api_setting_api_tokens_create.sync(client=client, body=body)

scope is admin, monitor, or node-sync. expires_at is a future Unix timestamp in milliseconds; 0 means no expiry. The plaintext token is returned only once, at creation — the panel stores only a hash.

Username and password

Log in with the panel admin credentials, then keep using the same Client instance. The session cookie the panel sets is stored in the client's underlying HTTP session and sent automatically on every subsequent call.

from x3ui import Client
from x3ui.api.authentication import post_login
from x3ui.api.inbounds import get_panel_api_inbounds_list
from x3ui.models.post_login_body import PostLoginBody

client = Client(base_url="https://panel.example.com:2053")

login = post_login.sync(
    client=client,
    body=PostLoginBody(username="admin", password="admin", two_factor_code=""),
)

if not login.success:
    raise RuntimeError(login.msg)

result = get_panel_api_inbounds_list.sync(client=client)
print(result.obj)

Pass an empty string as two_factor_code when two-factor authentication is disabled on the panel. When it is enabled, pass the current OTP code — it rotates every 30 seconds, so generate it immediately before logging in.

Reuse the same client object for everything that follows. Creating a second Client, or calling with_headers / with_cookies / with_timeout after logging in, produces a new instance with a fresh HTTP session that does not carry the session cookie, and calls will come back unauthenticated.

CSRF token for write operations

Session-based callers must send an X-CSRF-Token header on unsafe requests (POST, DELETE). Mint one after logging in and attach it to the existing session:

from x3ui.api.authentication import get_csrf_token

csrf = get_csrf_token.sync(client=client)
client.get_httpx_client().headers["X-CSRF-Token"] = csrf.obj

Setting the header on the underlying HTTP client keeps the session cookie intact, which with_headers would not. Bearer token callers skip this step entirely — CSRF is not enforced for token-authenticated requests.

Logging out

from x3ui.api.authentication import post_logout

post_logout.sync(client=client)

Common operations

List all clients

from x3ui.api.clients import get_panel_api_clients_list

result = get_panel_api_clients_list.sync(client=client)

For large panels use get_panel_api_clients_list_paged instead.

Look up a client by email

from x3ui.api.clients import get_panel_api_clients_get_email

result = get_panel_api_clients_get_email.sync("user@example.com", client=client)

The email is the client identifier in 3x-ui, not a real address.

Get traffic for a client

from x3ui.api.clients import get_panel_api_clients_traffic_email

result = get_panel_api_clients_traffic_email.sync("user@example.com", client=client)

Get subscription links for a client

from x3ui.api.clients import get_panel_api_clients_links_email

result = get_panel_api_clients_links_email.sync("user@example.com", client=client)

Reset a client's traffic counter

from x3ui.api.clients import post_panel_api_clients_reset_traffic_email

result = post_panel_api_clients_reset_traffic_email.sync("user@example.com", client=client)

Delete a client

from x3ui.api.clients import post_panel_api_clients_del_email

result = post_panel_api_clients_del_email.sync("user@example.com", client=client, keep_traffic=0)

Pass keep_traffic=1 to keep the accumulated traffic statistics after removal.

List clients that are currently online

from x3ui.api.clients import post_panel_api_clients_onlines

result = post_panel_api_clients_onlines.sync(client=client)

Get a single inbound

from x3ui.api.inbounds import get_panel_api_inbounds_get_id

result = get_panel_api_inbounds_get_id.sync(1, client=client)

Server status

from x3ui.api.server import get_panel_api_server_status

result = get_panel_api_server_status.sync(client=client)

Returns CPU, memory, uptime, network counters and Xray state.

Async usage

Every endpoint module exposes asyncio and asyncio_detailed alongside the sync variants. Import them under an alias to avoid shadowing the standard library module:

import asyncio as aio

from x3ui import AuthenticatedClient
from x3ui.api.clients import get_panel_api_clients_list

async def main():
    client = AuthenticatedClient(base_url="https://panel.example.com:2053", token="YOUR_API_TOKEN")
    result = await get_panel_api_clients_list.asyncio(client=client)
    print(result.obj)

aio.run(main())

Detailed responses

The plain sync and asyncio functions return the parsed model, or None when the status code is undocumented. Use the _detailed variants when you need the status code, headers or raw bytes:

from x3ui.api.server import get_panel_api_server_status

response = get_panel_api_server_status.sync_detailed(client=client)

print(response.status_code)
print(response.headers)
print(response.parsed)
print(response.content)

Error handling

By default, undocumented status codes yield None. To raise instead:

client = AuthenticatedClient(
    base_url="https://panel.example.com:2053",
    token="YOUR_API_TOKEN",
    raise_on_unexpected_status=True,
)

The raised exception is x3ui.errors.UnexpectedStatus, which carries status_code and content. Network timeouts surface as httpx.TimeoutException.

Client configuration

import httpx

client = AuthenticatedClient(
    base_url="https://panel.example.com:2053",
    token="YOUR_API_TOKEN",
    timeout=httpx.Timeout(30.0),
    verify_ssl=False,
    follow_redirects=True,
    headers={"User-Agent": "my-bot/1.0"},
)

verify_ssl=False disables certificate verification. Only use it against panels with self-signed certificates, never in production. To pin a custom CA, pass a path to the certificate file instead.

For anything not exposed directly, reach the underlying httpx client:

raw = client.get_httpx_client()

Endpoint groups

Endpoints live under x3ui.api.<group>, mirroring the tags in the specification:

Group Contents
authentication login, logout, CSRF token, 2FA
clients CRUD, traffic, groups, IP limits, HWIDs, bulk operations
inbounds CRUD, enable/disable, fallbacks, traffic resets, import/export
server status, Xray control, certificates, metrics, database
settings panel settings
xray_settings Xray core configuration
nodes multi-node management
hosts host entries
backup backup and restore
api_tokens token management
subscription_server subscription service
subscription_balancers subscription balancers
web_socket websocket endpoints

Module names follow the pattern <method>_<path>, so POST /panel/api/clients/add becomes x3ui.api.clients.post_panel_api_clients_add.

Known limitations

Response payloads arrive as {success, msg, obj} where obj is typed Any, because the upstream specification does not describe it. You get typed envelopes but untyped payloads.

Some request bodies are empty models for the same reason. Where a body model has no fields, the corresponding operation cannot be fully expressed through this client yet.

Do not run Python from inside the installed package directory. The package contains a types.py module that shadows the standard library types and causes a circular import.

Regenerating

The client is generated from openapi.json in this repository:

pip install openapi-python-client
openapi-python-client generate --path openapi.json --meta none --output-path x3ui --overwrite

Generated against 3x-ui version 3.x. Endpoints may differ on other panel versions.

Contributing

Improvements to openapi.json are the most valuable contribution: adding operationId values produces readable function names, and describing obj schemas makes responses properly typed. Open an issue or a pull request.

License

MIT

Release files for x3ui 1.0.1

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

Source distribution (sdist)

Source distribution for x3ui 1.0.1
File Size Uploaded
x3ui-1.0.1.tar.gz 149.5 kB Details

Built distribution (wheel)

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

Total release size: 681.9 kB

Release files / x3ui-1.0.1.tar.gz

Download URL x3ui-1.0.1.tar.gz
Size 149.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4f2c58805c252914e554c4cc0b6f6dfdf10ad9c0b8ba5aa3f6266f6745dd2bb4
BLAKE2b-256 checksum
How to use checksums
b2b3d183cd47edecf2d36153af63c51ab0c54c3baca8145a68788a1b5fb22fb8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 25, 2026.

Transparency log

Release files / x3ui-1.0.1-py3-none-any.whl

Download URL x3ui-1.0.1-py3-none-any.whl
Size 532.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bdc135652abebf997fe884a627b72c3f597cdd8871230deade612ce8220f12ae
BLAKE2b-256 checksum
How to use checksums
bebac8e6d6d9185a6fc61045a56bd069ec5b0c1ce68403dff19ca6d41156179a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 25, 2026.

Transparency log

Release history Release notifications | RSS feed

2.0.1

2 release files

2.0.0

2 release files

This release

1.0.1 This release

2 release files

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