Skip to main content

x3ui

PyPI Python CI License

Automate your 3x-ui panel from Python. Issue users, renew subscriptions, check usage, clean up expired accounts — without clicking through the web UI.

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

pip install x3ui

Connect

from x3ui import Panel

panel = Panel("https://panel.example.com:2053/yourpath")
panel.login("admin", "your-password")

The URL is exactly what you type in the browser, including the port and your panel's secret path.

For anything that runs unattended, use an API token instead — mint one under Settings → Security → API Token:

panel = Panel("https://panel.example.com:2053/yourpath", token="...")

A token is a full-admin credential. Keep it out of your source: read it from the environment.

Issue a user

from datetime import timedelta

panel.clients.add(
    "alice",
    inbound_ids=[3],
    total_gb=100,
    expires=timedelta(days=30),
    limit_ip=3,
)

for link in panel.clients.links("alice"):
    print(link)

The name is whatever identifies the user to you — the panel calls this field "email" but does not care whether it looks like one.

Traffic is gigabytes, expiry takes a timedelta from now or a datetime. Leave either out for unlimited. UUIDs, passwords and keys are generated by the panel.

links() returns the connection URLs for every inbound the user is on — that is what you send them. For a subscription URL instead, use the user's subId:

panel.clients.sub_links(panel.clients.get("alice").client.sub_id)

Don't know your inbound IDs? List them:

for inbound in panel.inbounds.list():
    print(inbound.id, inbound.remark, inbound.protocol, inbound.port)

Renew and top up

panel.clients.extend(["alice", "bob"], days=30, gigabytes=100)

Works on any number of users at once, and accepts negative values to take time or traffic away. Users on unlimited time or traffic are left alone rather than being converted to limited.

Renewing someone who ran out and got auto-disabled re-enables them.

To wipe a counter instead of adding to it:

panel.clients.reset_traffic("alice")
panel.clients.bulk_reset_traffic(["alice", "bob"])

Check usage

usage = panel.clients.traffic("alice")
print(usage.up, usage.down, usage.total, usage.expiry_time)

up and down are bytes used, total is the quota (0 means unlimited).

Who is connected right now:

print(panel.clients.online())

Where a user is connecting from:

print(panel.clients.ips("alice"))

Everyone at once, for a dashboard or a report:

for client in panel.clients.list():
    used = client.traffic.up + client.traffic.down
    print(client.email, used, client.enable)

Change and revoke

panel.clients.update("alice", limit_ip=1000, limit_hwid=10)
panel.clients.update("alice", password="new-secret", auth="new-secret")
panel.clients.update("alice", enable=False)

Only the fields you pass change; everything else stays as it is. Rotating a secret invalidates the user's existing links — send them fresh ones from links().

Cutting someone off, one or many:

panel.clients.bulk_disable(["alice", "bob"])
panel.clients.bulk_enable(["alice"])

panel.clients.delete("alice")
panel.clients.bulk_delete(["alice", "bob"], keep_traffic=True)

keep_traffic preserves the accounting rows after the user is gone, which matters if you bill from them.

Moving a user between inbounds without recreating them:

panel.clients.attach("alice", [7, 9])
panel.clients.detach("alice", [3])

Clean up

print(panel.clients.delete_depleted())
print(panel.clients.delete_orphans())

The first removes everyone out of traffic or past their expiry date; the second removes users left behind when their inbound was deleted. Both are destructive and report how many they took.

Server and Xray

status = panel.server.status()
print(status.cpu, status.mem.current, status.xray.state)

panel.server.restart_xray()

When something goes wrong

from x3ui import NotAuthenticated, X3uiError

try:
    panel.clients.add("alice", inbound_ids=[3])
except X3uiError as error:
    print(error.message)

X3uiError carries the message the panel itself would show — "email already in use", "port already in use", and so on. NotAuthenticated is raised when the session expired; log in again and retry. Connection problems raise httpx.TimeoutException.

Scripts that run on a schedule

import os
from datetime import datetime, timedelta, timezone

from x3ui import Panel

deadline = (datetime.now(timezone.utc) + timedelta(days=3)).timestamp() * 1000

with Panel(os.environ["PANEL_URL"], token=os.environ["PANEL_TOKEN"]) as panel:
    expiring = [
        client.email
        for client in panel.clients.list()
        if 0 < client.expiry_time < deadline
    ]
    if expiring:
        panel.clients.extend(expiring, days=30)

Used as a context manager, Panel closes its connection on exit. Token auth needs no login call and no session to expire, which is what you want from cron.

Self-signed certificate on the panel? Pass verify_ssl=False. Slow server? Pass timeout=60.

Anything else

The methods above cover day-to-day work. Nodes, hosts, backups, Xray config and the rest of the panel's 186 endpoints are available too:

from x3ui._generated.api.nodes import get_panel_api_nodes_list

print(get_panel_api_nodes_list.sync(client=panel.raw).obj)

Requires Python 3.10 or newer. Development notes and how to regenerate against your own panel are in CONTRIBUTING.md.

License

MIT

Release files for x3ui 2.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 2.0.1
File Size Uploaded
x3ui-2.0.1.tar.gz 190.9 kB Details

Built distribution (wheel)

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

Total release size: 831.7 kB

Release files / x3ui-2.0.1.tar.gz

Download URL x3ui-2.0.1.tar.gz
Size 190.9 kB
Tags Source
SHA-256 checksum
How to use checksums
bd24cbe928c9ecc461fe87132c891f77ec6a7259231fc080871736a483dba621
BLAKE2b-256 checksum
How to use checksums
5fd62b5a41a4ce720cdfb99fcfe51ba639c023cb8fe411d166f59984f523c825
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-2.0.1-py3-none-any.whl

Download URL x3ui-2.0.1-py3-none-any.whl
Size 640.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ad9d8c9250d43d0b4e258a26178021cabd69b63f1b4406c2b6f99057be105e54
BLAKE2b-256 checksum
How to use checksums
73abb99b43529caca56484170fff681b8f198486a5908a8bffd10ed8ef273e40
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

This release

2.0.1 This release

2 release files

2.0.0

2 release files

1.0.1

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