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 oem.dflop.top/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
If you don't know which to pick, use
Authorization: Bearer. It's the one form that works on every endpoint and with every SDK (point the OpenAI, Anthropic or Gemini SDK's base_url here and all three accept it), and it's what every example in these docs uses. The other three exist so that an SDK which already sends its own header needs no changes — they are fully supported, not second-class.
1. x-api-key header#
curl https://oemoemapi.dflop.top/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://oemoemapi.dflop.top/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://oemoemapi.dflop.top/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://oemoemapi.dflop.top/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 |
|---|---|
| Unsure / new integration | Authorization: Bearer — the universal form, accepted everywhere |
| OpenAI SDK | Authorization: Bearer (SDK default, nothing to change) |
| Anthropic SDK | x-api-key (SDK default, nothing to change) |
| Google Gemini SDK | x-goog-api-key (SDK default, nothing to change) |
| curl / by hand | Authorization: Bearer |
| Server-side code | env var → 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://oemoemapi.dflop.top/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 oem.dflop.top, 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 oem.dflop.top console itself uses cookies plus a CSRF token, but that's separate admin-panel auth and doesn't affect the gateway API.)