> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laozhang.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Token management API: manage API keys in code

> Issue API keys with a quota, expiry, and model allowlist from code through /api/token/, then reveal, update, disable, or delete them.

The token management API does what the Tokens page in the console does, from your own code. Typical uses are issuing a capped key to each downstream customer automatically, or disabling a batch of keys when a project ends. Calls made with these keys are charged to your account balance.

| Item | Details |
| - | - |
| Endpoint prefix | `https://api.laozhang.ai/api/token/` |
| Authentication | Account AccessToken; see [Get an AccessToken](/en/api-capabilities/balance-query#get-an-accesstoken) |
| Quota | 500,000 quota = 1 USD |
| Response shape | `{"success": true, "message": "", "data": ...}` |

<Warning>
  **The AccessToken has full control of your account. Keep it on the server.**

  * Never ship it to a browser or app, or hand it to a customer. Customers get only the API keys you create for them.
  * An invalid token returns HTTP 200 with `success: false`, so check `success` on every call.
</Warning>

## Endpoints

| Action | Request |
| - | - |
| List keys | `GET /api/token/?p=0` |
| Search by name | `GET /api/token/search?keyword=NAME&p=0` |
| Get one key | `GET /api/token/{id}` |
| Create a key | `POST /api/token/` |
| Reveal the full key | `POST /api/token/{id}/key` |
| Update a key | `PUT /api/token/` |
| Enable or disable | `PUT /api/token/?status_only=true` |
| Delete a key | `DELETE /api/token/{id}` |

List and search return 10 keys per page, with `p` starting at 0. The `key` in those results is masked; call the reveal endpoint to get the full key used for model calls.

## Fields for a new key

| Field | Type | Description |
| - | - | - |
| `name` | string | Key name, used for search; very long names are rejected |
| `remain_quota` | integer | Spending limit in quota; `500000` is 1 USD |
| `unlimited_quota` | boolean | `true` removes the limit and ignores `remain_quota` |
| `expired_time` | integer | Expiry in Unix seconds; `-1` never expires |
| `group` | string | Token group, usually `default`; other groups are listed in [Billing](/en/pricing) |
| `billing_type` | integer | Billing mode: `4` Usage first (recommended), `1` Usage-based, `2` Per-call |
| `models` | string | Model allowlist as comma-separated IDs; an empty string allows every model |
| `remark` | string | Optional note |

`billing_type` decides which models the key can call. Usage-based keys call only usage-based models, Per-call keys call only per-call models, and Usage first keys call both. [Billing](/en/pricing) explains the three modes.

## Create a key and reveal it

This script creates a key with a 5 USD limit, a 30-day expiry, and two allowed models, then reveals the full key. It needs `requests` (`python -m pip install requests`).

```python theme={null}
import os
import time

import requests

BASE = "https://api.laozhang.ai"
HEADERS = {"Authorization": os.environ["LAOZHANG_ACCESS_TOKEN"]}


def call(method, path, **kwargs):
    response = requests.request(method, BASE + path, headers=HEADERS, timeout=30, **kwargs)
    response.raise_for_status()
    payload = response.json()
    if not payload.get("success"):
        raise RuntimeError(f"{path}: {payload.get('message')}")
    return payload.get("data")


created = call("POST", "/api/token/", json={
    "name": "customer-acme",
    "remain_quota": 5 * 500_000,
    "unlimited_quota": False,
    "expired_time": int(time.time()) + 30 * 86400,
    "group": "default",
    "billing_type": 4,
    "models": "gpt-5.4-mini,gemini-3.8-flash",
    "remark": "Acme trial",
})
token_id = created["id"]

key = call("POST", f"/api/token/{token_id}/key")["key"]
print(token_id, key[:6] + "...")
```

The revealed key works as a model API key (`Authorization: Bearer <key>`), with or without an `sk-` prefix. Store it in your secret manager before you hand it out. Avoid calling the reveal endpoint repeatedly; it is strictly rate limited.

## Update, disable, and delete

An update replaces the whole key object. Read it with `GET /api/token/{id}`, change the fields you need, and `PUT` the full object back. Sending only a few fields can reset the others.

```python theme={null}
token = call("GET", f"/api/token/{token_id}")
token.pop("key", None)
token["remain_quota"] += 10 * 500_000   # add 10 USD
token["models"] = "gpt-5.4-mini,gemini-3.8-flash,deepseek-v4-flash"
call("PUT", "/api/token/", json=token)

call("PUT", "/api/token/?status_only=true", json={"id": token_id, "status": 2})  # disable
call("PUT", "/api/token/?status_only=true", json={"id": token_id, "status": 1})  # enable again

call("DELETE", f"/api/token/{token_id}")
```

Changes take effect immediately:

* A disabled or deleted key gets HTTP 401 `Invalid token.` on model calls.
* A model outside the allowlist gets HTTP 403 with a message that the token may not use that model.

## Errors

| Symptom | Cause and fix |
| - | - |
| HTTP 401 with `AUTH_REQUIRED` | The `Authorization` header is missing |
| HTTP 200 with `success: false` and "access token is invalid" | The AccessToken is wrong or was regenerated |
| HTTP 403 with "does not have write permission" | The token can only read, so it cannot create, update, or delete keys |
| `success: false` with a name-too-long message | Shorten `name` |
| `GET /api/token/{id}` returns "record not found" | The key was deleted or belongs to another account |

## FAQ

### Whose balance pays for a customer's key?

Yours. Set `remain_quota` on each key as its spending cap. When a key reaches its cap it stops working and cannot draw on the rest of your balance.

### How do I see how much each key has used?

`used_quota` in the list and detail responses is the key's total usage. To break it down by time or model, use the [log query API](/en/api-capabilities/log-query) with `token_name`.

### I set `model_limits`. Why is it ignored?

The model allowlist is read only from `models`. Put the allowed model IDs there as a comma-separated string.

## Related documentation

* Get an AccessToken and check your balance: [Balance API](/en/api-capabilities/balance-query)
* Export a key's call records: [Log query API](/en/api-capabilities/log-query)
* Create keys and set IP allowlists in the console: [Create, store, rotate, and revoke API keys](/en/faq/token-management)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.