Metadata-Version: 2.5
Name: newapi-python
Version: 0.1.0
Summary: Python client for the user-facing dashboard API of new-api instances
Author: Eight Labs
License-Expression: MIT
License-File: LICENSE
Keywords: ai-gateway,api,new-api,one-api,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: curl-cffi<1,>=0.10
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: cryptography>=43; extra == 'dev'
Requires-Dist: httpx<1,>=0.27; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Requires-Dist: twine>=5; extra == 'dev'
Description-Content-Type: text/markdown

# new-api

`newapi` is a Python client for the shared user-facing dashboard API exposed by [new-api](https://github.com/QuantumNous/new-api) 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 new-api deployments: account profile, quota balance, gateway tokens, usage logs and statistics, dashboard data, usable groups, optional top-up and online payment, redemption codes, subscriptions, check-in, and login-session management.

## Install

```bash
pip install newapi-python
```

Python 3.10 or newer is required.

## Authenticate with an existing session

The dashboard's access token is different from an `sk-...` gateway token. Browser deployments normally hold the session access token in memory after login or refresh, and keep the rotating refresh credential in the HttpOnly `new_api_refresh` cookie scoped to `/api/user/auth`.

```python
import os

from newapi import NewAPI

client = NewAPI(
    "https://newapi.example.com",
    access_token=os.environ["NEWAPI_ACCESS_TOKEN"],
    refresh_token=os.environ.get("NEWAPI_REFRESH_TOKEN"),
)
```

Pass either the instance origin or its full `/api` URL. Tokens are retained only in memory. If a refresh token is supplied, the client sends it as the `new_api_refresh` cookie, rotates the pair after an authenticated `401`, and refreshes proactively when `expires_at` is known. Users can also generate a long-lived system access token from the dashboard and pass it as `access_token` alone; such tokens cannot refresh browser sessions.

The default browser fingerprint is Chrome. Choose another `curl_cffi` fingerprint or configure proxies by supplying your own `curl_cffi.requests.Session`:

```python
from curl_cffi import requests

session = requests.Session(impersonate="safari")
client = NewAPI("https://newapi.example.com", session=session)
```

## Log in with username and password

```python
from newapi import NewAPI

with NewAPI("https://newapi.example.com") as client:
    user = client.login("root", "password")
    print(user.username, user.quota)
    print(client.is_authenticated)
```

When the instance enables login password encryption, the client fetches the RSA public key from `/api/user/login/encryption-key` and submits an RSA-OAEP/SHA-256 ciphertext, matching the browser. Pass `encrypt_password=False` to send the plaintext password instead. An instance with Cloudflare Turnstile enabled requires the corresponding proof:

```python
client.login("root", "password", turnstile_token="captcha-proof")
```

For a TOTP-enabled account, `login()` raises `TwoFactorRequired` and retains the flow token in memory:

```python
from newapi import NewAPI, TwoFactorRequired

client = NewAPI("https://newapi.example.com")

try:
    client.login("root", "password")
except TwoFactorRequired:
    client.complete_2fa("123456")
```

## Common operations

Resources are callable for their common list operation and also expose explicit methods.

```python
balance = client.balance()
print(balance.quota, balance.amount)

status = client.status()
groups = client.groups()
print(groups["vip"].ratio, groups["vip"].desc)

models = client.account.models()
print(client.account.aff_code())
```

`balance()` reports raw quota integers plus `amount`, `used_amount`, and `aff_amount` as `Decimal` values converted with the instance's `quota_per_unit` (default `500000`).

## Gateway tokens

`tokens` and `keys` refer to the same resource. Instances return masked key values in list responses; call `reveal()` to fetch the full `sk-...` value.

```python
first_page = client.tokens(page_size=50)
for token in first_page:
    print(token.id, token.name, token.status, token.remain_quota)

all_tokens = client.tokens.all()

client.tokens.create("automation", group="default")
token, key = client.tokens.create_and_reveal("automation", group="default")
client.tokens.update(token.id, name="nightly automation")
client.tokens.disable(token.id)
client.tokens.enable(token.id)
client.tokens.delete(token.id)

print(client.tokens.reveal(token.id))
print(client.tokens.reveal_batch([1, 2, 3]))
print(client.tokens.auto_groups())
```

Token creation does not echo the new record from the instance, so `create()` returns `None`; `create_and_reveal()` creates the token, finds it in the list, and returns the record together with its full key. `update()` reads the current token first and resubmits preserved values for any field you leave out, because the instance replaces the whole record on update. `expired_time` accepts a Unix timestamp, a `datetime`, or `-1` for no expiry. `allow_ips` accepts a comma-separated string or a sequence of strings.

## Usage history

`history`, `usage`, and `logs` refer to the same resource.

```python
from datetime import datetime, timedelta, timezone

end = datetime.now(timezone.utc)
start = end - timedelta(days=7)

page = client.history(
    log_type="consume",
    start_timestamp=start,
    end_timestamp=end,
    page_size=100,
)

for record in page:
    print(record.created_at, record.model_name, record.quota, record.prompt_tokens)

for record in client.history.iter(log_type="consume", page_size=100):
    process(record)

stats = client.logs.stat(log_type="consume", start_timestamp=start, end_timestamp=end)
print(stats.quota, stats.rpm, stats.tpm)
```

`log_type` accepts an integer or one of `topup`, `consume`, `manage`, `system`, `error`, `refund`, and `login`. Timestamps accept `datetime` objects or Unix seconds.

## Dashboard data

```python
rows = client.dashboard.quota_data(start_timestamp=start, end_timestamp=end)
flow = client.dashboard.flow_data(start_timestamp=start, end_timestamp=end)
```

Both endpoints limit the time span to one month; `flow_data` requires explicit positive bounds.

## Top-up, redemption, and payment

Online payment is optional and must be enabled and configured by the instance administrator. Inspect the top-up configuration before offering a recharge:

```python
info = client.topup.info()

if info.enable_stripe_topup:
    link = client.payment.stripe_pay(10)
    print(link)

if info.enable_online_topup:
    amount = client.payment.epay_amount(10)
    checkout = client.payment.epay_pay(10, "alipay")
    print(checkout.url, checkout.params)
```

Creating an order does not credit the balance; the configured provider must confirm payment before the instance completes the top-up. Track the resulting orders and redeem codes with:

```python
orders = client.topup.orders()
result = client.topup.redeem("REDEMPTION-CODE")
print(result.quota)
```

## Subscriptions

```python
plans = client.subscriptions.plans()
overview = client.subscriptions.self()
client.subscriptions.set_preference("balance_first")
client.subscriptions.purchase_with_balance(plans[0].id)
```

## Check-in

```python
status = client.account.checkin_status()
if status.enabled:
    result = client.account.checkin()
    print(result.quota_awarded, result.checkin_date)
```

## Login sessions

```python
for entry in client.account.sessions():
    print(entry.sid, entry.login_method, entry.current)

client.account.revoke_session("sid-from-another-device")
client.account.revoke_other_sessions()
```

These endpoints require a browser login session; long-lived system access tokens are rejected.

## Fork-specific endpoints

`request()` provides the same authentication, envelope handling, refresh behavior, and error mapping for relative endpoints that are not part of the stable resource API.

```python
result = client.request("GET", "user/self")
```

Absolute URLs and parent-path traversal are rejected so a session token cannot be redirected outside the configured API root.

## Errors

HTTP failures and new-api envelope failures use typed exceptions:

```python
from newapi import APIError, AuthenticationError, NewAPIError, RateLimitError

try:
    client.tokens.create("automation")
except RateLimitError as error:
    print(error.retry_after)
except AuthenticationError:
    client.login("root", "password")
except NewAPIError as error:
    print(error)
```

Most new-api business errors arrive as HTTP `200` with `success: false`; they raise `APIError` with the instance's message. Middleware failures use real HTTP statuses and map to `AuthenticationError`, `PermissionDeniedError`, `RateLimitError`, and friends. Object representations redact fields that commonly contain credentials.

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