Python 88.3%
TypeScript 7.6%
Shell 4.1%
1# API key handling23**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)4**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`5**Last verified:** 2026-09-1967## Key types you will meet89| | OpenAI | Anthropic | xAI | Gemini |10|---|---|---|---|---|11| 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 |12| 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 |13| 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 |14| 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) |15| 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 |16| 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) |1718## Rules19201. **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 through `scripts/live.py` / `scripts/lib.sh`, which also **mask** `sk-…` patterns and auth headers in anything saved (`mask()`).212. **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.223. **Service-account keys for servers**, not personal keys: they survive employee departures and can be governed (OpenAI API Key Governance can enforce this).234. **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.245. **Restrict network origin** where possible: OpenAI IP allowlist (org/project). Anthropic: pin egress from your servers and monitor `anthropic-organization-id`/`anthropic-workspace-id` in responses to detect misrouted keys.256. **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.267. **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).278. **On compromise**: revoke immediately (OpenAI safety-best-practices "Revoke compromised API keys"), rotate, review usage pages and audit logs (`GET /v1/organization/audit_logs`; xAI `GET /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 — set `oldSecretExpireTime` short 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.289. **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 in `scripts/live.py` cover all of these) and use platform secret scanning.2910. **Timeouts + retries must not amplify a bad key**: 401/403 are never retried, and neither is **xAI's 400 `invalid-argument`** nor Gemini's 400/403 key errors (see `docs/architecture/resilience.md`) — a retry storm on an invalid key is noise that hides real problems.3011. **xAI: scope keys with ACLs, not just with limits.** `api-key:endpoint:chat` + `api-key:model:grok-4.3` for a chat-only service; grant `image`/`*` only where needed; add `qpm`/`tpm` caps per key so a leaked key cannot drain the team's TPM; introspect with `GET /v1/api-key` in your health check and alert on `api_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.3112. **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-key` header 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.3213. **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: 0` 429s on Pro models) before any production or privacy-sensitive traffic; for a contractual zero-data-retention posture use Vertex AI.3334## Checklist3536- [ ] Keys only in secret manager / `.env` (600); `.env` in `.gitignore`; `.env.example` has placeholders only.37- [ ] Separate keys per environment; project (OpenAI) / workspace (Anthropic) / team + ACL-scoped key (xAI) / Cloud project + restricted auth key (Gemini) per app.38- [ ] Service-account keys with expiry (OpenAI governance, Anthropic expiration, xAI `expireTime`, Gemini auth keys); org-level max lifetime where available.39- [ ] Network restrictions: OpenAI IP allowlist; Gemini application restrictions (IP); xAI mTLS for enterprise teams.40- [ ] Rotation runbook written and rehearsed (xAI: rotate with overlap then expire the old secret; Gemini: create → restrict → deploy → delete); revocation path tested.41- [ ] Admin / Management keys separate, read-only in automation, never on app servers; xAI Management key stored apart from inference keys.42- [ ] Logs masked (`Authorization`, `x-api-key`, `x-goog-api-key`, `sk-…`, `xai-…`, `AIza…`, `?key=`), request bodies not logged verbatim; Gemini never called with `?key=`.43- [ ] End-user attribution (`safety_identifier` OpenAI/xAI, `metadata.user_id` Anthropic, `labels.safety_identifier` Gemini) so a bad actor is blocked without rotating the shared key.44- [ ] Gemini: billing enabled and the free-tier data-use terms understood before sending anything sensitive; Cloud-console key restrictions applied.45- [ ] Clients classify xAI 400 "Incorrect API key" and Gemini 400/403 key errors as credential failures (no retry, alert).46