API key handling
Status: DOCUMENTED (xAI GET /v1/api-key and the 400-on-bad-key behaviour LIVE_VERIFIED 2026-09-19; Gemini header auth LIVE_VERIFIED, ?key= never used)
Sources: https://developers.openai.com/api/docs/guides/production-best-practices#api-keys · https://developers.openai.com/api/docs/guides/error-codes (401 "IP not authorized", "Incorrect API key") · https://developers.openai.com/api/docs/guides/safety-best-practices#revoke-compromised-api-keys · https://platform.claude.com/docs/en/api/errors (401 authentication_error, key expiration) · https://platform.claude.com/docs/en/api/admin (API key scope) · https://platform.claude.com/docs/en/api/rate-limits · xAI: https://docs.x.ai/developers/management-api-guide (create keys with acls[], qps/qpm/tpm, expireTime; rotate with oldSecretExpireTime; propagation), https://docs.x.ai/developers/rest-api-reference/inference/other (GET /v1/api-key, GET /v1/me), https://docs.x.ai/developers/debugging (400 invalid-argument "Incorrect API key provided"), https://docs.x.ai/developers/faq/security (mTLS host, ZDR), https://docs.x.ai/developers/advanced-api-usage/mtls · Gemini: https://ai.google.dev/gemini-api/docs/api-key (x-goog-api-key, standard vs authorization keys, "Restricting and securing your keys", unrestricted keys rejected, leaked keys blocked), https://ai.google.dev/gemini-api/docs/oauth, https://ai.google.dev/gemini-api/docs/ephemeral-tokens, https://ai.google.dev/gemini-api/terms (Unpaid Services data use), https://ai.google.dev/gemini-api/docs/usage-policies · this repo's CLAUDE.md + scripts/live.py
Last verified: 2026-09-19
Key types you will meet
| OpenAI | Anthropic | xAI | Gemini | |
|---|---|---|---|---|
| Runtime key | Project API key (sk-proj-…), user- or service-account-owned; Authorization: Bearer |
Workspace API key (sk-ant-api…), scope organization or workspace; x-api-key + anthropic-version |
Team API key (xai-…) created in the console or via the Management API; Authorization: Bearer on REST, WebSocket and gRPC; no API version or beta headers. Keys carry ACLs (api-key:endpoint:* / api-key:endpoint:chat, api-key:model:* / api-key:model:grok-4.6) — a key created via the API has no access by default — plus optional per-key qps/qpm/tpm caps and expireTime; can be disabled/blocked; the whole team can be blocked |
AI Studio API key (AIza…); x-goog-api-key header (recommended, used by the SDKs and this atlas). ?key= in the URL also works but leaks into proxy/CDN/referrer logs — never use it. Standard keys identify a project only; authorization (auth) keys are bound to a Google Cloud service account (granular IAM, fast leaked-key enforcement) and are the default since 2026-05-28; unrestricted standard keys are already rejected, standard keys are rejected from September 2026 — migrate. OAuth 2.0 / ADC (Authorization: Bearer <token>) for owner-scoped resources; ephemeral tokens (POST /v1beta/auth_tokens) for browser Live-API clients. The OpenAI-compat layer (/v1beta/openai/*) needs the key as Authorization: Bearer instead |
| Admin / management key | Admin API key for /v1/organization/* |
Admin API key for /v1/organizations/* with read:* / write:* scopes |
Management key (console → Settings → Management Keys; SCOPE_TEAM or SCOPE_ORGANIZATION; requires "Management Keys Read + Write") for https://management-api.x.ai — a different host and key type; an inference key there → 401 {"code": 16, "message": "Invalid bearer token…"}. This atlas has none (ACCOUNT_RESTRICTED) |
Google Cloud IAM on the project (apikeys.keys.*, iam.serviceAccounts.*, serviceusage.services.enable) and Cloud Billing; no separate "admin API" — the Gemini API only exposes /v1/webhooks, agents, credentials, triggers under the same key |
| Introspection | Usage page | Console | GET /v1/api-key → redacted_api_key, name, user_id, team_id, acls[], api_key_blocked, api_key_disabled, team_blocked, create_time (live: acls ["api-key:model:*","api-key:endpoint:*"]); GET /v1/me → zdr_status (no_zdr / zdr), team, key flags (API key or OAuth). Management: GET /auth/management-keys/validation, GET /auth/api-keys/{id}/propagation |
none for the key itself; AI Studio shows keys (100 keys / 50 projects displayed; keys restricted to other APIs are hidden). The 429 body's quotaDimensions.model and the X-Gemini-Service-Tier header tell you which tier/model the key hit |
| Bad-key behaviour | 401 "Incorrect API key" | 401 authentication_error |
400 invalid-argument "Incorrect API key provided. You can obtain an API key from https://console.x.ai." (missing header → 401 unauthenticated:no-credentials; missing ACL → 403) — a client that only treats 401 as "fix credentials" will misclassify this |
400 INVALID_ARGUMENT "API key not valid. Please pass a valid API key." or 403 PERMISSION_DENIED; leaked key → 403 "Your API key was reported as leaked. Please use another API key." (auto-blocked by Google's scanners) |
| Governance | API Key Governance (service-account-only, max lifetime), IP allowlist | Workspaces, key expiration, anthropic-workspace-id header |
ACL scoping per key, per-key rate caps, expireTime, rotation with overlap (POST /auth/api-keys/{id}/rotate, oldSecretExpireTime default 24 h, max 7 d), team-level mTLS (https://mtls.api.x.ai: client certificate in addition to the key), regional hosts (us.api.x.ai), team-wide Zero Data Retention |
Cloud-console API restrictions (restrict the key to the Generative Language API only) and application restrictions (IP addresses, HTTP referrers, Android/iOS apps); auth keys inherit the service account's IAM; leaked-key auto-blocking; 10 projects creatable from AI Studio |
| Tracking | Usage page per key | Console per workspace/key | console Usage Explorer (by key, model, request IP, cluster, token type) and POST /v1/billing/teams/{id}/usage (Management API); per-request usage.cost_in_usd_ticks; x-request-id |
AI Studio rate-limit/usage dashboards (per project, not per key); Cloud Billing reports; body responseId (no request-id header) |
Rules
- Never in code, repos, images, client bundles, or prompts. Environment variables or a secret manager only. This atlas keeps keys in
.env(mode 600, gitignored) and injects them throughscripts/live.py/scripts/lib.sh, which also masksk-…patterns and auth headers in anything saved (mask()). - One key per deployment/environment. OpenAI: separate staging and production projects; Anthropic: separate workspaces. A leaked staging key must not reach production data or budgets.
- Service-account keys for servers, not personal keys: they survive employee departures and can be governed (OpenAI API Key Governance can enforce this).
- Set an expiry at creation and rotate on a schedule: create the replacement → deploy → verify → revoke the old one (OpenAI's documented sequence). Enforce a maximum lifetime org-wide so forgotten keys die.
- Restrict network origin where possible: OpenAI IP allowlist (org/project). Anthropic: pin egress from your servers and monitor
anthropic-organization-id/anthropic-workspace-idin responses to detect misrouted keys. - Never send a runtime key to the browser or mobile app. Proxy through your backend; the backend adds
safety_identifier(OpenAI) /metadata.user_id(Anthropic) so abuse is attributable per end-user, not per key. - Admin keys are not runtime keys: they cannot call inference (Anthropic Admin API keys are org-management only) and must live in a separate, more restricted secret with GET/LIST-only automation (this repo's rule: never destructive Admin actions).
- On compromise: revoke immediately (OpenAI safety-best-practices "Revoke compromised API keys"), rotate, review usage pages and audit logs (
GET /v1/organization/audit_logs; xAIGET /audit/teams/{id}/events+ Usage Explorer by request IP; Gemini Cloud audit logs) for the exposure window, and check spend limits were not raised. xAI's rotate endpoint keeps the old secret valid for up to 7 days — setoldSecretExpireTimeshort once the new key is deployed. Gemini blocks keys it finds leaked on its own ("reported as leaked") — treat that 403 as an incident, not a bug. - Detect leaks pre-commit: scan for
sk-,sk-ant-,sk-proj-,sk-admin-,whsec_,xai-,AIza(Google API keys),AQ.(Gemini auth-key/ephemeral-token style) patterns and?key=query strings (the mask regexes inscripts/live.pycover all of these) and use platform secret scanning. - Timeouts + retries must not amplify a bad key: 401/403 are never retried, and neither is xAI's 400
invalid-argumentnor Gemini's 400/403 key errors (seedocs/architecture/resilience.md) — a retry storm on an invalid key is noise that hides real problems. - xAI: scope keys with ACLs, not just with limits.
api-key:endpoint:chat+api-key:model:grok-4.3for a chat-only service; grantimage/*only where needed; addqpm/tpmcaps per key so a leaked key cannot drain the team's TPM; introspect withGET /v1/api-keyin your health check and alert onapi_key_blocked/team_blocked. Inference keys and Management keys live in different secrets; the Management key can mint inference keys, so it is the more sensitive one. - Gemini: restrict every key in the Cloud console (API restriction → Generative Language API; application restriction → your server IPs) and prefer auth keys bound to a dedicated service account with only the Generative Language roles. Never embed a key in a mobile/web client — use ephemeral tokens for the Live API and your backend for everything else. Note that the
x-goog-api-keyheader is stripped by fewer intermediaries than a?key=parameter, but any proxy that logs headers still sees it: TLS end-to-end, no header logging. - Gemini free tier is a data-use decision, not just a quota. Under the Gemini API Terms, Unpaid Services (free tier, AI Studio) let Google use prompts, uploaded content and responses "to provide, improve, and develop Google products", with human review — do not send confidential or personal data through a free-tier key. Paid Services are not used for product improvement but are logged for abuse monitoring for a limited period (usage policies: 55 days). EEA/UK/CH end users must be served with Paid Services only. Enable billing (a Cloud Billing account also lifts
limit: 0429s on Pro models) before any production or privacy-sensitive traffic; for a contractual zero-data-retention posture use Vertex AI.
Checklist
- Keys only in secret manager /
.env(600);.envin.gitignore;.env.examplehas placeholders only. - Separate keys per environment; project (OpenAI) / workspace (Anthropic) / team + ACL-scoped key (xAI) / Cloud project + restricted auth key (Gemini) per app.
- Service-account keys with expiry (OpenAI governance, Anthropic expiration, xAI
expireTime, Gemini auth keys); org-level max lifetime where available. - Network restrictions: OpenAI IP allowlist; Gemini application restrictions (IP); xAI mTLS for enterprise teams.
- Rotation runbook written and rehearsed (xAI: rotate with overlap then expire the old secret; Gemini: create → restrict → deploy → delete); revocation path tested.
- Admin / Management keys separate, read-only in automation, never on app servers; xAI Management key stored apart from inference keys.
- Logs masked (
Authorization,x-api-key,x-goog-api-key,sk-…,xai-…,AIza…,?key=), request bodies not logged verbatim; Gemini never called with?key=. - End-user attribution (
safety_identifierOpenAI/xAI,metadata.user_idAnthropic,labels.safety_identifierGemini) so a bad actor is blocked without rotating the shared key. - Gemini: billing enabled and the free-tier data-use terms understood before sending anything sensitive; Cloud-console key restrictions applied.
- Clients classify xAI 400 "Incorrect API key" and Gemini 400/403 key errors as credential failures (no retry, alert).