Incorrect API key provided is one of four documented OpenAI 401 conditions, and it means the string that reached the API is not a key OpenAI recognizes. The causes OpenAI lists are a typo or extra space, a key belonging to a different organization or project, a deleted or deactivated key, and a revoked key cached locally. In practice the top cause is environment precedence: a stale OPENAI_API_KEY exported in a shell profile silently outranks the correct value in your .env. The nastier sibling is a 403 rather than a 401, returned when the key is valid but its project lacks access to the model you asked for, and it carries code: model_not_found which sends people hunting the wrong problem entirely.
- The message is a 401 with
code: invalid_api_key, and thetypeisinvalid_request_error, notauthentication_error. - OpenAI documents four distinct 401s. The heading in the docs tells you which one you have.
- Environment beats config file in almost every SDK. A stale export in
~/.zshrcis the single most common cause. - A key belongs to one project. Sending it with a mismatched
OpenAI-Projectheader is a 401. - A valid key with no model access returns 403, not 401, carrying
code: model_not_found. - Keys are shown once. Losing one means creating a new one, not recovering the old one.
sk-proj-,sk-svcacct-, andsk-admin-are community-documented prefixes. OpenAI publishes no official table.
Incorrect API key provided: sk-***
The body is a 401 with the key redacted down to its first and last few characters, which is itself useful: those visible characters are enough to tell you which key was actually sent.
{
"error": {
"message": "Incorrect API key provided: sk-qVL45***************************************D1Vi. You can find your API key at https://platform.openai.com/account/api-keys.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
The docs list four 401 conditions and one 403, each with its own heading; a second 403 shape (code model_not_found on a project without model access) shows up in the wild but not in the docs. Matching your response to the right row is the whole diagnostic.
| Status and heading | Means |
|---|---|
401 Invalid Authentication | The credential is not usable at all: revoked, wrong org, or missing the permission that endpoint needs |
401 Incorrect API key provided | The string is not a key OpenAI recognizes |
401 You must be a member of an organization to use the API | The account behind the key belongs to no organization |
401 IP not authorized | Your request IP is outside the project or org IP allowlist |
403 Country, region, or territory not supported | Geography, not credentials |
403 with code: model_not_found | Valid key, but the project cannot use that model |
The four causes OpenAI lists, and the one it does not
Under the Incorrect API key provided heading the docs name four causes. They are worth quoting because each maps to a different check.
- "There is a typo or an extra space in your API key." This covers the whole family of copy-paste damage: a trailing newline from
echo, a leading space from a terminal selection, a soft-wrapped key that picked up a line break in a config file. - "You are using an API key that belongs to a different organization or project." The most common version of this is an org you forgot you had.
- "You are using an API key that has been deleted or deactivated." Deletion is immediate and irreversible.
- "An old, revoked API key might be cached locally." The docs suggest clearing your browser cache, which applies to dashboard sessions. For code, the cache is your environment.
The cause OpenAI does not document, because it is not their layer, is the one that produces the most confused bug reports.
Environment precedence
Nearly every SDK and framework resolves the key from OPENAI_API_KEY in the process environment, and nearly every .env loader refuses to overwrite a variable that is already set. So an export you added to ~/.zshrc six months ago silently wins over the correct key in the project file you are staring at, and the redacted prefix in the error message is your only clue that a different key was ever involved.
# what is in the environment right now?
echo "${OPENAI_API_KEY:0:8}...${OPENAI_API_KEY: -4}"
# where did it come from?
grep -rn "OPENAI_API_KEY" ~/.zshrc ~/.bashrc ~/.profile ~/.zshenv 2>/dev/null
# does it have trailing whitespace? a clean key prints exactly its length
printf %s "$OPENAI_API_KEY" | wc -c
echo "$OPENAI_API_KEY" | od -c | tail -2 # look for \n or \r at the end
# does the API agree it is valid?
curl -sS https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY" | jq '.error // "key is valid"'
Key types, projects, and the headers that go with them
Modern OpenAI keys are scoped to a project rather than to a user, and that scoping is where most of the remaining 401s live.
| Prefix | Scope | Typical use |
|---|---|---|
sk-proj- | One project | The default for a key created in the dashboard today |
sk-svcacct- | A service account inside one project | Machine identity for a deployment, survives a person leaving |
sk-admin- | Organization level | The Admin API: managing projects, keys, and members. Not for inference |
sk- (legacy) | User, across the org | Older keys, being phased out |
Two optional headers select which org and project a request bills to, and getting them wrong produces two different failures.
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "OpenAI-Organization: $ORGANIZATION_ID" \
-H "OpenAI-Project: $PROJECT_ID" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.6-terra","input":"ping"}'
- Header disagrees with the key: 401. Sending
OpenAI-Projectfor a project the key does not belong to has been reported asOpenAI-Project header should match project for API key. The fix is to drop the header, since a project key already identifies its project, or to send the matching one. - Key valid, project lacks model access: 403. This is the one that misdirects people. The body reads
Project \`proj_id\` does not have access to model \`gpt-5.2\`withcode: model_not_found, so it reads like a model problem and is actually a project permissions problem. Check the project's allowed models before you go looking for the model ID. - Admin keys are not inference keys. An
sk-admin-key on/v1/responsesgives you a permissions failure, and the message about permissions rather than about the key is the tell.
Rotation without an outage
A key is displayed once, at creation. There is no way to view it again, so rotation is always create-then-delete, never reveal-and-copy.
Create the replacement first
In the same project, so scope and model access carry over unchanged. Give it a name that says where it runs, because a list of six keys named "key" is how you end up unable to delete any of them.
Deploy the new key everywhere before deleting anything
Two keys in one project both work simultaneously. There is no reason to accept downtime here, and no documented propagation delay to wait out.
Watch usage on the old key until it goes flat
Keys created after 20 December 2023 have per-key usage tracking on by default. Older keys need it enabled manually. A key still showing traffic is a deployment you forgot.
Delete the old key, then confirm the failure mode
Deletion is immediate and irreversible. After deleting, a request with the old key returns the 401 this page is about, which is the confirmation you want.
A decision table for the 401 in front of you
| What you see | Most likely cause | Check |
|---|---|---|
Works in curl, fails in your app | Environment precedence: a different key is being picked up | Print the first 8 and last 4 characters from inside the process |
| Worked yesterday, fails today, nothing changed | Key deleted, or a teammate rotated it | Dashboard key list and its usage graph |
| Fails only in CI | Secret not set, or set on the wrong environment | Echo the length, never the value, from the CI step |
| Fails only in production | Deploy still holds the pre-rotation key | Redeploy after confirming the secret store |
| 401 mentioning organization | Account is in no org, or the org header is wrong | Drop OpenAI-Organization and retry |
| 403 mentioning a model | Project lacks access to that model | The project's allowed-models setting, not the model ID |
| 403 mentioning country or region | Geography | Not a credential problem. Nothing to fix in code |
401 IP not authorized | IP allowlist on the project or org | Add the egress IP, or remove the allowlist |
The single highest-yield check on that list is the first one. Print the redacted key from inside the running process, compare it to the one you intended, and about two thirds of these end there.
Questions people ask
Why does my OpenAI API key work in curl but not in my code?
Almost always because your code is not using the key you think it is. An OPENAI_API_KEY exported in a shell profile takes precedence over the value in a .env file, because most dotenv loaders refuse to overwrite an already-set variable. Print the first eight and last four characters from inside the running process and compare them to the redacted key in the error message.
What is the difference between sk-proj and sk keys?
An sk-proj- key is scoped to a single project, which is the default for keys created in the dashboard today. A bare sk- key is a legacy user key that worked across the organization. Project scoping is why a key can be perfectly valid and still 401 when sent with a mismatched OpenAI-Project header. Note that OpenAI does not publish an official prefix table, so treat these as community-documented.
Can I view an OpenAI API key after creating it?
No. It is shown once at creation and never again. If you lose it, create a new key in the same project, deploy it, then delete the old one. Two keys in one project work at the same time, so rotation needs no downtime.
Why am I getting model_not_found with a 403 instead of a 401?
Because the key is valid and the project it belongs to is not allowed to use that model. The body reads "Project proj_id does not have access to model X" with code model_not_found, which sends people looking for a typo in the model ID. Check the project allowed-models setting instead.
Does a trailing newline break an OpenAI API key?
Yes. OpenAI lists "a typo or an extra space in your API key" as a documented cause of this error, and a trailing newline from echo or a Windows carriage return in a .env file behaves the same way. Pipe the variable through od -c and look at the last bytes.
How do I fix "You must be a member of an organization to use the API"?
The account the key belongs to is not attached to any organization. Either the org was deleted, or you were removed from it, or the key predates the current account structure. Sign in to the platform dashboard and check your organization membership, then create a fresh key inside a project you actually belong to.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.
- OpenAI API error codes the four 401 headings and their documented causes
- OpenAI API authentication reference Authorization, OpenAI-Organization, and OpenAI-Project headers
- OpenAI workload identity federation short-lived credentials instead of long-lived keys
- OpenAI production best practices per-key usage tracking defaults
- openai-python issue 1968 captured invalid_api_key response body