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:

  1. Create Key — a name (required), an optional allowed_models allowlist, and an optional expiry
  2. View any time — the detail page can reveal the raw key (stored encrypted server-side)
  3. Check usage — per-request history with model, tokens and cost
  4. Revoke — any key can be disabled or deleted immediately

What you can configure per key#

FieldMeaningDefault
nameHuman-readable name, for auditingrequired
allowed_modelsModel 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_allowedomitted (all allowed)
expires_atExpiry timenone
enabledEnabled / disabledtrue

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_at and enabled; 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-keyx-goog-api-key?key=Authorization: Bearer

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/models accepts only the Authorization: Bearer and x-api-key headers — ?key= is not supported there.

What we recommend#

ClientRecommended method
OpenAI SDKAuthorization: Bearer (SDK default)
Anthropic SDKx-api-key (SDK default)
Google Gemini SDKx-goog-api-key (SDK default, nothing to change)
curl / by handx-api-key
Server-side codeenv 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:

EndpointBilled by
chat / embeddingstokens (actual usage × unit price; when the upstream returns cached_tokens the cached rate applies automatically, nothing to switch on)
imagesper image (unit price × count — see Media APIs)
videoper second (settled on the actual generated duration, refunded in full on failure)

Each chat request goes through two steps:

  1. Pre-charge — estimate the worst-case cost and compare it to your balance. Over budget returns 402 and nothing is sent upstream.
  2. 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_tokens in full. Without max_tokens it estimates min(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-wire max_tokens to 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/messages returns the Anthropic shape (type: "billing_error", no code field) and /v1beta returns 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:

SituationSuggested key setup
Personal development / experimentingone key with no model restriction
Production serviceallowed_models locked to 1–2 models, named distinctly for auditing
One-off calls / spikesa short-lived key with expires_at, revoked when done
Team useone key per person, named after the user (note: they share one balance)

Security advice#

  1. Never commit a key to git, put it in a screenshot, or paste it into Slack
  2. Inject it as an env var with direnv, the 1Password CLI or Doppler
  3. Revoke immediately in the console if you suspect a leak
  4. Keep keys in server-side code — calling from the browser exposes them
  5. If a browser must call the API, proxy through your backend
  6. 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.)