OpenAI key types — project, service account, admin

Last verified

Quick answer: sk-proj-… makes model calls. sk-admin-… reads billing and manages keys. They are not interchangeable, and one of them is much more dangerous than the other.

The types

PrefixTypeScopeCan do
sk-proj-…Project API keyOne projectInference
sk-admin-…Admin keyOrganisationUsage, cost, key/project/member management
sk-… (legacy)User keyPersonal accountInference — being phased out in favour of project keys

Service accounts are a variation on the first: a project key that belongs to a service rather than a person, so it survives that person leaving the company. If a key is in production and tied to an individual's account, it is one offboarding away from an outage.

Which do you need?

Calling a model → project key. Never an admin key; admin keys cannot do inference anyway.

Reading spend → admin key. A project key cannot see billing data at all — this is the wall most people hit when they first try to build a cost dashboard.

Revoking keys programmatically → admin key.

A production service → a project key belonging to a service account, one per service, so you can attribute usage and revoke precisely.

Blast radius

The reason to care about the distinction:

A leaked project key means someone can spend your money on inference. Bad, bounded by your spend limit, fixed by revoking that key.

A leaked admin key means someone can read all your billing data and delete every key in the organisation — an instant, total outage across everything you run. It cannot spend money on tokens directly, so the damage is disclosure and destruction rather than cost.

The practical rule that falls out of this: never store an admin key next to your inference keys. Different secret store, different environment, different repository. Otherwise a single leak escalates from "rotate one key" to "the whole organisation".

One key per service

Not bureaucracy — it is what makes an incident survivable.

With one shared key everywhere, "this key is compromised" means taking down everything at once and having no idea which service was the source. With one key per service, you can see which key generated the anomalous usage, revoke just that one, and keep the rest running.

It also gives you per-service cost attribution for free, via group_by=api_key_id on the Usage API — no proxy, no code changes.

Naming

Name every key at creation for where it will live: api-prod, worker-batch, ci-integration-tests, tkn-ios. Not key1, not test, not blank.

The full value is shown once and never again. Six months later you'll be looking at a list of redacted keys trying to work out which is safe to delete, and the name is the only thing that answers it. An unnamed key you're afraid to delete is worse than no key.

Sources