Skip to main content

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, 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()

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.

Development

python -m pip install -e '.[dev]'
pytest
ruff check .
mypy
python -m build
twine check dist/*

HAR captures are ignored by Git because they can contain live session credentials.

Release files for sub2api 0.1.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 sub2api 0.1.0
File Size Uploaded
sub2api-0.1.0.tar.gz 17.2 kB Details

Built distribution (wheel)

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

Total release size: 34.3 kB

Release files / sub2api-0.1.0.tar.gz

Download URL sub2api-0.1.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d5726592582194d7b48323aae5635d96e16dc09d7d78972a3ae6bdb9cf860908
BLAKE2b-256 checksum
How to use checksums
a05fa5070640ae8401cf70aa326552e1058b02bd3654a2790f2540f6f996c5e7
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.1.0-py3-none-any.whl

Download URL sub2api-0.1.0-py3-none-any.whl
Size 17.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f7eaae0738dd48f5b56f6492c8f183fc44d4f89f47c89327c7fafb7beb1f130d
BLAKE2b-256 checksum
How to use checksums
0b6dc00f2a51eed13b90f7e0d15df5ddbb0839aa157fd55954d8677c8562b29f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.0

Release history Release notifications | RSS feed

0.2.0

2 release files

0.1.1

2 release files

This release

0.1.0 This release

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