Skip to main content
Every request authenticates with a bearer API key.
x-api-key: pyai_live_... is an accepted alias for environments where setting an Authorization header is awkward.
Keys are opaque strings (up to 512 characters). Never parse, split, or decode them. They are self-validating and work on every PyAI surface the instant they are created, there is no activation or propagation delay.

Environments

No signup: POST /v1/sandbox/keys returns a pyai_test_ key in api_key. It works on the first call, skips the credit gate, and is bounded by a daily unit cap. See the quickstart. Because the mint asks for no email, password or card, it is capped per source network: 2 keys per rolling 24 hours, after which it returns 429 sandbox_limit_reached until the oldest ages out. Keep the key you mint (in CI, put it in a secret) rather than minting a fresh one per run, and see account-creation limits if you share an egress and need the network raised. Create a longer-lived key in the console. The secret is shown once. Store it as an environment variable, never in source control.

Scopes

Keys carry scopes that gate which products they can call: GET /v1/models and GET /v1/voices are catalog reads, any active key may call them. A request whose key lacks the required scope returns 403 forbidden. Cue is unavailable. There is no active Cue scope or grounding behavior on the Hear streaming route.

WebSocket authentication

Browsers can’t set headers on a WebSocket upgrade, so pass the key as a subprotocol — two values, the pyai.v1 marker first and your key second:
Offer both. RFC 6455 makes the server echo the subprotocol it selects, so PyAI selects pyai.v1 and never reflects your key in the response — your key does not appear in browser devtools or in any intermediary’s response-header log. A server can only select a value the client actually offered: send the key alone and there is nothing safe left to select, so the 101 carries no Sec-WebSocket-Protocol at all, which browsers accept but Node’s ws rejects with Server sent no subprotocol. The PyAI SDKs already send both. Server-side clients may instead append ?api_key=... to the URL. Never put the key in any other query parameter.

Rotation & revocation

Each key in the console has rotate and revoke controls.
  • Rotate issues a new secret and invalidates the old one.
  • Revoke disables the key everywhere within 60 seconds.
After revocation, calls with the old key return 401 unauthorized.
If a key is ever exposed, revoke it immediately and mint a new one, there is no penalty for rotating often.

What errors look like

Data-plane auth failures use the OpenAI envelope. Branch on error.code. Do not retry these. Missing or revoked key (401). The gateway code is invalid_api_key (or missing_api_key when the header is absent):
Key is valid but missing a product scope (403). A sandbox mint hitting Clone looks like this (speak:clone is not on POST /v1/sandbox/keys):
Call GET /v1/me and read scopes before you retry. Signup keys include speak:clone; sandbox mint keys do not. A pyai_live_ key on an org with no prepaid credit (402):
Sandbox pyai_test_ keys skip the credit gate. They return 429 daily_cap_exceeded when the daily unit cap is hit instead. See Errors and limits for the full code table and Pricing for current rates and included usage.