Appearance
Authentication
Every call to /v1 carries an API key as a bearer token:
http
Authorization: Bearer sk_live_...The OpenAI clients do this for you when you pass api_key=. There is no other way in: no query parameter, no cookie, no signed URL. That holds for the WebSocket endpoint too, where a key in the query string would end up in proxy and browser logs.
Keys belong to a project
text
Organization — the account: members, billing, limits
└─ Project — one application or workload
└─ API keyA key names its project, so usage, request logs and spending are attributed without you sending anything extra. That is the reason to run one project per application rather than one key for everything: it is the only way to tell later which workload spent the money.
Test and live
Keys come in two environments, and the secret says which:
| Prefix | Environment |
|---|---|
sk_live_… | Live |
sk_test_… | Test |
Both call the same models on the same hardware. A test key is charged exactly like a live one — there is no free sandbox. The environment is a label for separating usage, request logs and spending in the dashboard, not a different service. Treat a test key with the same care as a live one.
Handling keys
- The secret is shown once, when the key is created. We store a hash, so a lost key is replaced rather than recovered.
- Keys carry an optional expiry. An expired key fails with
api_key_expired. - Revoking is immediate; the next request fails with
api_key_revoked. - Rotating revokes the old secret and issues a replacement with the same name and environment, in one step. There is no overlap window: it is the right move for a key you believe is compromised, and the wrong one for routine hygiene. For a zero-downtime change, create a second key, deploy it, then revoke the first.
- Keep keys server-side. A key in a browser bundle or a mobile binary is a published key, and it can spend your balance.
Checking a key
GET /v1/me answers what a key is attached to, without spending anything:
json
{
"organization_id": "org_01J8...",
"project_id": "proj_01J8...",
"api_key_id": "key_01J8...",
"environment": "test"
}Useful in a deploy check: it distinguishes "the key is wrong" from "the request is wrong" before any audio moves.
Failures
| Code | Status | Meaning |
|---|---|---|
missing_api_key | 401 | No Authorization header arrived |
invalid_api_key | 401 | Unknown secret, or one from another deployment |
api_key_revoked | 401 | The key was revoked |
api_key_expired | 401 | The key is past its expiry |
All four are deliberately hard to tell apart from the outside in timing terms, and none of them say whether the organization exists.