Authentication
API key format, four ways to authenticate, key management, balance and billing
API key format#
An API key (shown in the console as a "sub-key") looks like:
sk-gpushare-{64 hex characters}
Example: sk-gpushare-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Total length is 76 characters (the 12-character sk-gpushare- prefix plus 64 hex characters). Configure key-validation and secret-scanning regexes against that length.
The raw key is stored server-side encrypted with AES-256-GCM and can be revealed again at any time on its detail page in the console. Only a handful of early hash-only keys can't be revealed — if you lose one of those, create a new key.
The same key works on every public endpoint: all four chat protocol endpoints, the image / video / music APIs and the knowledge base API & MCP.
Creating and managing keys#
Go to dianqi.zsopc.com/dashboard/keys:
- Create Key — a name (required), an optional
allowed_modelsallowlist, and an optional expiry - View any time — the detail page can reveal the raw key (stored encrypted server-side)
- Check usage — per-request history with model, tokens and cost
- Revoke — any key can be disabled or deleted immediately
What you can configure per key#
| Field | Meaning | Default |
|---|---|---|
name | Human-readable name, for auditing | required |
allowed_models | Model allowlist. NULL or omitted means every model is allowed. ⚠️ Do not pass an empty array — an empty array denies every model and each request returns 400 model_not_allowed | omitted (all allowed) |
expires_at | Expiry time | none |
enabled | Enabled / disabled | true |
There is no budget setting on a key. Budget lives in the account balance, shared by every key — see Balance and billing below. The only per-key controls are
allowed_models,expires_atandenabled; they exist for access control and auditing, not budget isolation.
Four ways to authenticate#
The gateway looks for a key in this order and stops at the first hit — any one of them works:
x-api-key → x-goog-api-key → ?key= → Authorization: Bearer
1. x-api-key header (recommended)#
curl https://dianqi.zsopc.com/v1/messages \
-H "x-api-key: sk-gpushare-xxx" \
...
The Anthropic SDK and Claude Code use this by default. It's the most direct, and the least likely to be swallowed by an intermediary.
2. x-goog-api-key header#
curl https://dianqi.zsopc.com/v1beta/models/gemini-2.5-flash:generateContent \
-H "x-goog-api-key: sk-gpushare-xxx" \
...
The default header of Google's genai SDK — Gemini SDK users need no changes at all. The value never lands in a URL access log, so there's no leakage risk.
3. ?key= query parameter#
curl "https://dianqi.zsopc.com/v1beta/models/gemini-2.5-flash:generateContent?key=sk-gpushare-xxx" \
...
The Gemini REST-style alternative. Not recommended by hand — the key ends up in access logs and browser history.
4. Authorization: Bearer header#
curl https://dianqi.zsopc.com/v1/chat/completions \
-H "Authorization: Bearer sk-gpushare-xxx" \
...
The OpenAI SDK uses this by default.
Exception:
GET /v1/modelsaccepts only theAuthorization: Bearerandx-api-keyheaders —?key=is not supported there.
What we recommend#
| Client | Recommended method |
|---|---|
| OpenAI SDK | Authorization: Bearer (SDK default) |
| Anthropic SDK | x-api-key (SDK default) |
| Google Gemini SDK | x-goog-api-key (SDK default, nothing to change) |
| curl / by hand | x-api-key |
| Server-side code | env var → x-api-key or Authorization: Bearer |
Use an env var, never a hardcoded key#
# ~/.bashrc
export PLATFORM_API_KEY=sk-gpushare-xxx
client = OpenAI(
api_key=os.environ["PLATFORM_API_KEY"],
base_url="https://dianqi.zsopc.com/v1",
)
Balance and billing (one wallet)#
The platform is prepaid, not post-pay.
The billing model#
Budget lives in your account balance (a USD wallet) and every API key draws on that same balance. A key has no budget pool of its own — it's an access credential.
- Trial credit: 121.32 on sign-up, enough to run every getting-started example
- Top up: dflop.top/dashboard/billing (Stripe, 404.4 minimum; same SSO account as dianqi.zsopc.com, shared balance)
- Check it: the console dashboard shows your balance and per-key usage in real time
Billing units depend on the endpoint:
| Endpoint | Billed by |
|---|---|
| chat / embeddings | tokens (actual usage × unit price; when the upstream returns cached_tokens the cached rate applies automatically, nothing to switch on) |
| images | per image (unit price × count — see Media APIs) |
| video | per second (settled on the actual generated duration, refunded in full on failure) |
Each chat request goes through two steps:
- Pre-charge — estimate the worst-case cost and compare it to your balance. Over budget returns 402 and nothing is sent upstream.
- Settle — after the turn, deduct the real token usage from the balance and write a usage log entry.
How pre-charge estimates#
Pre-charge is deliberately worst-case:
- Input ≈ the messages JSON byte length ÷ 4
- Output counts your explicit
max_tokensin full. Withoutmax_tokensit estimatesmin(the model's default_max_tokens, 32768). The model's real output ceiling can be far higher — 128K for gpt-5.x and Claude Opus/Sonnet 5, 131072 for GLM and Grok, 256K for Kimi — and when you omit the parameter the gateway raises the on-wiremax_tokensto that ceiling for you. The estimate deliberately doesn't assume the ceiling, so a low balance isn't rejected for no reason.
If the estimate exceeds your balance you get a 402 even when the real usage would have been far smaller. When your balance is tight, pass a smaller explicit max_tokens to get through pre-charge. Note also that settle does not gate again, so under concurrent requests the balance can dip slightly negative (the next request is then rejected).
Out of balance#
Returns HTTP 402 Payment Required. OpenAI shape (/v1/chat/completions and friends):
{"error": {"message": "Insufficient balance. Please top up and try again.", "type": "insufficient_quota", "code": "quota_exceeded"}}
- When the balance is ≤ 0 you get the 402 during authentication, before pre-charge even runs
/v1/messagesreturns the Anthropic shape (type: "billing_error", nocodefield) and/v1betareturns the Gemini shape (status: "RESOURCE_EXHAUSTED"). Side-by-side comparison: Error handling
Fix: top up at dflop.top/dashboard/billing. Creating a new key does not help — every key shares one balance, so when it runs out they all 402 together.
Working with several keys#
A key is a credential, not a budget pool, so multiple keys are about access isolation and attribution:
| Situation | Suggested key setup |
|---|---|
| Personal development / experimenting | one key with no model restriction |
| Production service | allowed_models locked to 1–2 models, named distinctly for auditing |
| One-off calls / spikes | a short-lived key with expires_at, revoked when done |
| Team use | one key per person, named after the user (note: they share one balance) |
Security advice#
- Never commit a key to git, put it in a screenshot, or paste it into Slack
- Inject it as an env var with direnv, the 1Password CLI or Doppler
- Revoke immediately in the console if you suspect a leak
- Keep keys in server-side code — calling from the browser exposes them
- If a browser must call the API, proxy through your backend
- Because a key can be revealed in the console at any time, the security of the console account itself matters just as much — protect the SSO account
CSRF / Origin#
Gateway endpoints do not enforce Origin or CSRF tokens — authentication is the key alone. A leaked key therefore means full exposure.
(The dianqi.zsopc.com console itself uses cookies plus a CSRF token, but that's separate admin-panel auth and doesn't affect the gateway API.)