Authentication
One key, one header, server-side only.
Every published operation takes the same credential:
There is one scheme and no alternative, which means there is no decision for you
to make and one code path for each of us to get right. The contract names it
ApiKeyAuth; the API reference on this site shows it on every operation.
The one rule that matters more than the rest
The key is a server-side secret. It authenticates your account, it spends your credits, and anything that holds it can do everything you can do.
So it must never reach a browser bundle, a mobile binary, a mobile app’s configuration, a public repository, a client-side environment variable, or a URL query string. If you are building a front end, the supported shape is your own backend holding the key and exposing a narrow endpoint of its own: Serving a browser or mobile client.
We do not publish a browser snippet for this API. Plenty of API quickstarts do, because it demos beautifully, and the result is keys in published bundles.
Live and test keys
A key belongs to one environment of your account. A key that starts with pk_live_
runs real analyses and spends credits. A key that starts with pk_test_ runs the same
lifecycle with fixture results and charges nothing; it can never read live jobs or
assets, and a live key can never read test ones. See Test mode.
Create and revoke keys on the API keys page of the
Platform; see Get access.
Scopes
A key carries scopes. Each product submission requires that product’s scope, and cancelling or deleting a job requires the scope of the job’s product. This table is generated from the API contract:
A key’s available scopes depend on its environment and the grants selected when it is
created. INSUFFICIENT_SCOPE means the key is valid but does not include the grant this
product operation requires.
Uploads, polling and result reads are authenticated but not scope-gated. A key that could submit work but not read the result it created would be useless, and a scope every key must hold is not a permission — it is noise.
What a rejection tells you
The first row is deliberately one code: a credential that cannot be used answers
401 INVALID_API_KEY whatever the reason, and only a usable key without the
operation’s scope, or called from outside its allowlist, is a 403. Branch on the status, and check the key you configured.
The last row is the one to handle deliberately. It is a 503 and not a 401
because your credential was never judged — nothing is wrong with it. A client that
treated it as an authentication failure would discard a working key during an outage
of ours, which is precisely the wrong reaction.
Full list, generated from the code that raises them: Error codes.
Isolation
Assets and jobs belong to the environment whose key created them. A valid key from
another account, or from your own other environment, polling one of your job ids gets
404 JOB_NOT_FOUND, and reading one of your asset ids gets 404 ASSET_NOT_FOUND — the
same answers as an id that never existed.
That is not vagueness; 403 there would confirm the resource exists and turn its id
into a lookup oracle.
Rotation and hygiene
- Read the key from your secret manager or environment at process start; do not commit it, and do not bake it into an image.
- Rotate on any suspicion of exposure, and on staff changes. Plan for two keys to be valid at once during a rotation so you are never down to zero.
- Log the
request_idfrom a failed call. Never log the key, and never log it partially — a prefix plus a length is a meaningful head start for an attacker.