OpenAI Error 401: Invalid Authentication — Causes and Fixes

Quick Fix
  • Regenerate the key in the API keys dashboard and paste the new one — revoked, deleted, or mistyped keys are the #1 cause.
  • Confirm the env var is actually loaded — a missing OPENAI_API_KEY sends Bearer None / Bearer undefined, which 401s.
  • Match the key to the right org/project — a key scoped to one project 401s against another. Strip stray whitespace and quotes.

HTTP 401 Unauthorized means OpenAI could not authenticate your request. Unlike a 429, retrying won’t help — the credential itself is wrong, missing, malformed, or not authorized for what you’re calling. The fix is almost always a five-minute configuration change once you know which of the handful of causes you’ve hit.

What this error means

A 401 is returned before your request is processed. OpenAI never even looks at your prompt — it rejects you at the door. The body usually carries code: "invalid_api_key":

Typical 401 body
{
"error": {
  "message": "Incorrect API key provided: sk-abc***. You can find your API key at https://platform.openai.com/account/api-keys.",
  "type": "invalid_request_error",
  "code": "invalid_api_key"
}
}

Note what this is not: it’s not a rate limit (429), not a permissions/region problem (403), and not a server fault (5xx). A 401 is purely “I don’t believe you are who your key says you are.” See the table below for how to tell them apart fast.

Common causes

The usual suspects

Identify which one applies before you change anything — the fix differs.

  • Revoked, rotated, or deleted key. The key worked yesterday, then someone rotated it (or it was auto-revoked after being committed to a public repo — OpenAI scans GitHub and disables leaked keys). Generate a fresh one.
  • Env var not loaded. Your code reads os.environ["OPENAI_API_KEY"] or process.env.OPENAI_API_KEY, but the .env file wasn’t loaded, the variable name is misspelled, or your deploy platform didn’t inject it. The SDK then sends an empty bearer token.
  • Whitespace, quotes, or a truncated key. A trailing newline from echo "key" > .env, surrounding quotes pasted into a secrets UI, or a key cut short on copy all corrupt the header silently.
  • Wrong organization or project header. A project-scoped key (sk-proj-…) used with a mismatched OpenAI-Project / OpenAI-Organization value fails auth. This bites teams whose members belong to several orgs.
  • Using the wrong key type or account. A session token, a frontend “publishable” placeholder, or a key from a different account/tenant won’t authenticate against the API.
  • Pointing at the wrong endpoint. Using a standard OpenAI key against an Azure OpenAI endpoint (or vice versa) 401s — Azure uses a different auth scheme (api-key header) and base URL.

How to fix it

Work from “is the key valid at all?” outward to “is it scoped correctly?”

Step 1 — Prove the key works in isolation

cURL — does the bare key authenticate?
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-o /dev/null -w "HTTP %{http_code}\n" -s
# 200 => the key is valid; your app is mangling it or sending the wrong one.
# 401 => the key itself is the problem; regenerate it.

If the bare key 401s here, the problem is the credential — regenerate it in the dashboard. If it returns 200, the key is fine and your application is corrupting it or sending a different value.

Step 2 — Fail fast in your app

Validate the key at startup so a misconfiguration crashes at boot with a clear message instead of mid-request in production.

Python — validate on startup, strip whitespace
import os
from openai import OpenAI, AuthenticationError

key = os.environ.get("OPENAI_API_KEY")
if not key:
  raise RuntimeError("OPENAI_API_KEY is not set in this environment.")
if key != key.strip():
  print("Warning: key had surrounding whitespace; stripping it.")

client = OpenAI(api_key=key.strip())

try:
  client.models.list()  # cheap authenticated call
  print("Auth OK")
except AuthenticationError as e:
  raise SystemExit(f"OpenAI auth failed — check the key/org/project: {e}")
JS / Node — guard env + set org/project only if scoped
import OpenAI from "openai";

if (!process.env.OPENAI_API_KEY) {
throw new Error("OPENAI_API_KEY missing — is your .env loaded in this environment?");
}

const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY.trim(),
// Set these ONLY if your key is scoped to a specific org/project.
// A mismatched value here causes a 401 even with a valid key.
organization: process.env.OPENAI_ORG_ID,
project: process.env.OPENAI_PROJECT_ID,
});

try {
await client.models.list();
console.log("Auth OK");
} catch (err) {
console.error("OpenAI auth failed:", err?.status, err?.code, err?.message);
process.exit(1);
}

Step 3 — Azure OpenAI uses different auth

If you’re on Azure, the standard SDK config won’t work — Azure authenticates with an api-key header against your resource endpoint and a deployment name:

Python — Azure OpenAI auth
from openai import AzureOpenAI

client = AzureOpenAI(
  api_key=os.environ["AZURE_OPENAI_API_KEY"],   # NOT your platform.openai.com key
  api_version="2024-10-21",
  azure_endpoint="https://YOUR-RESOURCE.openai.azure.com",
)
# Use your *deployment* name as the model:
resp = client.chat.completions.create(model="my-gpt-4o-deployment",
                                    messages=[{"role": "user", "content": "Hi"}])

How 401 relates to 403 and 429

These three get confused constantly. The code field and status tell you which lane you’re in:

OpenAI auth/limit errors at a glance (as of May 2026).
StatusMeaningRetry?Fix
401 Key invalid / missing / unauthenticatedNoFix or regenerate the key; check org/project
403 Authenticated but not allowed (region, model, permission)NoEnable access / use a supported region or model
429 Authenticated, but rate-limited or out of quotaYes (rate) / No (quota)Back off, or add credits — see the 429 guide

If you authenticated successfully but still can’t call a specific model, that’s a 403, not a 401 — different fix.

How to prevent it

  • Store keys in a secret manager, never in source control. Rotate on a schedule and after any suspected leak.
  • Validate on startup (Step 2) so misconfig fails loudly at deploy time, not during a user request.
  • Use project-scoped keys per environment (dev/staging/prod) so a leaked dev key can be revoked without taking prod down.
  • Avoid echo for .env files — it appends a newline. Use your secret store’s UI or printf without a trailing \\n.

Auth failures look similar across providers but use different headers:

  • Anthropic uses an x-api-key header (plus anthropic-version), not Authorization: Bearer — a 401 there usually means a missing or wrong x-api-key. See Anthropic Claude rate limits for the header layout, and the OpenAI → Anthropic migration guide for the full auth remap.
  • Google Gemini authenticates via an API key query param or OAuth; a 401/403 there typically means the key isn’t enabled for the Generative Language API.

What to do next

  1. Run the cURL check (Step 1) to isolate key vs. app.
  2. If the key 401s, regenerate it; if it passes, fix env loading / whitespace / org-project scoping in your app.
  3. Add startup validation so this never reaches production silently again.
  4. Auth fixed but requests still failing? The next most common error is rate limiting — see the OpenAI 429 fix and the OpenAI rate limits reference.

Frequently asked questions

Why does my key work locally but 401 in production?
The env var almost certainly isn't loaded in production. Confirm your deploy platform injects OPENAI_API_KEY (correct name, correct environment) and that nothing strips, quotes, or truncates it. A startup validation check surfaces this at deploy time.
Can a 401 be a rate-limit issue?
No. Rate limiting returns 429. A 401 is always an authentication problem — the credential is wrong, missing, or malformed. If you authenticated but lack access to a model or region, that's a 403.
Do I need the organization and project headers?
Only if your key is scoped to a specific project (sk-proj-…), or your account belongs to multiple organizations. A mismatched header on a scoped key causes a 401 even though the key is valid.
OpenAI disabled my key after I pushed it to GitHub — what now?
OpenAI scans public repositories and automatically revokes leaked keys. Generate a new one, remove the key from git history, and move it into a secret manager or environment variable so it never enters source control again.
I'm on Azure OpenAI and getting 401s with my normal key — why?
Azure OpenAI is a separate service with its own credentials and auth scheme (an api-key header against your resource endpoint, using a deployment name). Your platform.openai.com key won't authenticate there; use the Azure resource key and endpoint.
How do I verify the key without spending tokens?
Call the models endpoint (client.models.list() or GET /v1/models). It's authenticated but doesn't run a completion, so it confirms the credential without incurring generation cost.