sub2api
sub2api is a Python client for the shared user-facing panel API exposed by Sub2API instances. One client object represents one user's in-memory dashboard session. Requests use curl_cffi with Chrome browser impersonation by default.
The library targets operations present on standard Sub2API deployments: account balance, optional payment deposits, payment orders, platform quotas, usage history and statistics, API keys, groups, subscriptions, announcements, and redemption.
Install
pip install sub2api
Python 3.10 or newer is required.
Authenticate with an existing session
The dashboard's access token is different from an sk-... gateway API key. Browser deployments normally store the panel tokens under auth_token and refresh_token in local storage.
import os
from sub2api import Sub2API
client = Sub2API(
"https://sub2api.example.com",
access_token=os.environ["SUB2API_ACCESS_TOKEN"],
refresh_token=os.environ.get("SUB2API_REFRESH_TOKEN"),
)
print(client.me().email)
print(client.balance().balance)
Pass either the instance origin or its full /api/v1 URL. Tokens are retained only in memory. If a refresh token is supplied, the client rotates the token pair after an authenticated 401. Supplying expires_at as a Unix timestamp also enables proactive refresh.
The default browser fingerprint is Chrome. Choose another curl_cffi fingerprint or configure proxies by supplying your own curl_cffi.requests.Session:
from curl_cffi import requests
session = requests.Session(impersonate="safari")
client = Sub2API("https://sub2api.example.com", session=session)
Log in with email and password
from sub2api import Sub2API
with Sub2API("https://sub2api.example.com") as client:
user = client.login("person@example.com", "password")
print(user.username)
print(client.is_authenticated)
An instance with CAPTCHA enabled requires the corresponding proof:
client.login(
"person@example.com",
"password",
turnstile_token="captcha-proof",
)
For a TOTP-enabled account, login() raises TwoFactorRequired and retains the temporary challenge in memory:
from sub2api import Sub2API, TwoFactorRequired
client = Sub2API("https://sub2api.example.com")
try:
client.login("person@example.com", "password")
except TwoFactorRequired:
client.complete_2fa("123456")
Common operations
Resources are callable for their common list operation and also expose explicit methods.
balance = client.balance()
quotas = client.account.platform_quotas()
groups = client.groups()
group_rates = client.groups.rates()
first_page = client.keys(page_size=50, status="active")
for api_key in first_page:
print(api_key.id, api_key.name, api_key.group.name)
all_keys = client.keys.all()
resolved = client.keys.with_group_multipliers()
for item in resolved:
print(
item.api_key.key,
item.group_id,
item.base_multiplier,
item.custom_multiplier,
item.effective_multiplier,
)
multiplier_by_key = client.keys.multiplier_map(key_by="key")
multiplier_by_id = client.keys.multiplier_map(key_by="id")
created = client.keys.create("automation", group_id=groups[0].id)
client.keys.update(created.id, name="nightly automation")
client.keys.set_status(created.id, active=False)
client.keys.delete(created.id)
all() follows pagination until every key has been fetched. with_group_multipliers() joins each key to its group and reports the base, user-specific, and effective rate; the user-specific rate from /groups/rates takes precedence. multiplier_map() returns the effective rate keyed by the API key value, key ID, or name. Name collisions raise an error instead of silently overwriting an entry.
API key values are available through api_key.key, but object representations redact fields that commonly contain credentials.
Usage history
history and usage refer to the same resource.
from datetime import date, timedelta
end = date.today()
start = end - timedelta(days=7)
page = client.history(
start_date=start,
end_date=end,
page_size=100,
sort_by="created_at",
sort_order="desc",
)
for record in page:
print(record.created_at, record.model, record.actual_cost)
for record in client.history.iter(page_size=100):
process(record)
stats = client.usage.stats(start_date=start, end_date=end)
dashboard = client.usage.dashboard()
trend = client.usage.trend(start_date=start, end_date=end, granularity="day")
models = client.usage.models(start_date=start, end_date=end)
snapshot = client.usage.snapshot(start_date=start, end_date=end)
Other shared resources
active_subscriptions = client.subscriptions(active=True)
announcements = client.announcements()
client.announcements.mark_read(announcements[0].id)
result = client.redeem("REDEMPTION-CODE")
redemption_history = client.redeem.history()
Deposits and payment
Payment is optional and must be enabled and configured by the instance administrator. Inspect the checkout configuration before offering a deposit:
checkout = client.payment.checkout_info()
if checkout.methods:
order = client.deposit(
10,
payment_type="stripe",
return_url="https://app.example.com/payment/result",
)
print(order.order_id, order.pay_url, order.qr_code, order.client_secret)
Available payment methods depend on the instance and can include alipay, wxpay, stripe, and airwallex. The returned order contains the provider-specific checkout data: a hosted payment URL, QR code, Stripe client secret, or WeChat OAuth/JSAPI payload. Creating the order does not credit the balance; the configured provider must confirm payment before the instance completes the deposit.
Use the payment resource to inspect and manage the order:
pending = client.payment.get(order.order_id)
verified = client.payment.verify(order.out_trade_no)
orders = client.payment.list(status="COMPLETED", order_type="balance")
client.payment.cancel(order.order_id)
An instance without payment configuration returns its normal typed API error, including PAYMENT_DISABLED or NO_AVAILABLE_INSTANCE, rather than silently treating a deposit as successful. Public order recovery is also available when a checkout flow has a signed resume token:
public_order = client.payment.resolve_public(order.resume_token)
Fork-specific endpoints
request() provides the same authentication, envelope handling, timezone parameter, refresh behavior, and error mapping for relative endpoints that are not part of the stable resource API.
result = client.request("GET", "some-fork-specific-endpoint")
Absolute URLs and parent-path traversal are rejected so a session token cannot be redirected outside the configured API root.
Errors
HTTP and Sub2API envelope failures use typed exceptions:
from sub2api import AuthenticationError, RateLimitError, Sub2APIError
try:
client.keys.create("automation")
except RateLimitError as error:
print(error.retry_after)
except AuthenticationError:
client.login("person@example.com", "password")
except Sub2APIError as error:
print(error)
Remote plaintext HTTP is rejected by default because it exposes login credentials and tokens. Localhost HTTP is allowed for development; other HTTP instances require allow_insecure=True.
Release files for sub2api 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sub2api-0.2.0.tar.gz | 19.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sub2api-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.4 kB
Release files / sub2api-0.2.0.tar.gz
| Download URL | sub2api-0.2.0.tar.gz |
|---|---|
| Size | 19.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b9e611b28e8322751622fba23b465c3c7c0feb4da1f89eb05ab8fc7047aa8e7
|
|
BLAKE2b-256 checksum How to use checksums |
ef4e57d525044940343cef3921ded0076069da2224430188d6decfaf71b99ba7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Release files / sub2api-0.2.0-py3-none-any.whl
| Download URL | sub2api-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4b186e4badaf586db5634da2ebd7bd5269e4287d45664fcd3d19062a37678af4
|
|
BLAKE2b-256 checksum How to use checksums |
e65c9d868d7748d3bb86461e795bfc6bd18bced19b77df415b003b468a31e939
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|