This guide shows you how to fix the OpenAI 401 Unauthorized error in Python in
under ten minutes. A 401 always means one thing: the server could not confirm who
you are. Your request arrived, but the API key it carried was missing, mistyped,
wrong, expired, or aimed at the wrong account. The good news is that the model
never ran, so a 401 costs you nothing and is safe to retry once fixed.
We will look at the exact message you see, then walk through numbered fixes. Each
fix has runnable Python 3.10+ you can paste and run. If you are brand new to keys
and requests, read Understanding LLM APIs
first, since this page assumes you already have a key and the openai SDK installed.
The exact error you see
When the key fails, the openai SDK raises an AuthenticationError and prints
something close to this:
openai.AuthenticationError: Error code: 401 - {'error': {'message': 'Incorrect
API key provided: sk-abcd****. You can find your API key at
https://platform.openai.com/account/api-keys.', 'type': 'invalid_request_error',
'param': None, 'code': 'invalid_api_key'}}
The key signal is the number 401 and the class name AuthenticationError. If
you instead see 429, that is a rate or quota problem covered in
Fix the 429 Rate-Limit Error in Python,
not an auth problem. Confirm you really have a 401 before applying these fixes.
Prerequisites
You only need what differs from the parent section: the openai SDK, python-dotenv
to load your key, and a recent Python. Pin versions so your results match this guide.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "openai>=1.40" "python-dotenv>=1.0"
Create a .env file next to your script with your key on one line, no quotes and
no spaces:
OPENAI_API_KEY=sk-your-real-key-goes-here
Add .env to your .gitignore so the key never lands in version control. A leaked
key can be used by strangers and billed to you.
echo ".env" >> .gitignore
Fix 1 — Confirm the key is actually loaded
The most common cause of a 401 is that your program never read the key at all. A
.env file does nothing by itself; you must load it. This check prints the key's
length and a masked prefix so you can see whether it arrived without ever exposing
the full secret.
import os
from dotenv import load_dotenv
load_dotenv() # reads .env from the current working directory
key = os.environ.get("OPENAI_API_KEY")
if not key:
print("No key found. .env not loaded, or the variable name is wrong.")
else:
print(f"Key loaded: length {len(key)}, starts with {key[:6]}...")
A healthy key prints a length in the dozens and starts with sk-. If you see
"No key found", the .env file is in a different folder than where you run the
script, or the variable is misnamed. Run the script from the folder that holds
.env, or pass an explicit path: load_dotenv("/full/path/to/.env").
Fix 2 — Catch the error and read its details
Wrap your call so the program tells you precisely what went wrong instead of
crashing. The openai SDK gives the status code and an exact message you can act on.
import os
from dotenv import load_dotenv
from openai import OpenAI, AuthenticationError
load_dotenv()
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
try:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "ping"}],
)
print(resp.choices[0].message.content)
except AuthenticationError as err:
print(f"401 auth failed (status {err.status_code}): {err.message}")
Read the printed message. Phrases like "Incorrect API key provided" point to a wrong or mistyped key (Fix 3). Phrases mentioning an organization or project point to an account mismatch (Fix 4).
Fix 3 — Check the key is correct, current, and clean
A 401 with "Incorrect API key" means the string is wrong. Three things cause this: a typo, a key that was revoked or regenerated, or hidden characters copied along with the key. This check catches the hidden-character case, which is easy to miss.
import os
from dotenv import load_dotenv
load_dotenv()
key = os.environ.get("OPENAI_API_KEY", "")
problems = []
if not key.startswith("sk-"):
problems.append("key does not start with 'sk-'")
if key != key.strip():
problems.append("key has leading or trailing whitespace")
if '"' in key or "'" in key:
problems.append("key contains quote characters from the .env file")
print("Problems:" if problems else "Key format looks clean.")
for p in problems:
print(" -", p)
If the format looks clean but the call still fails, the key itself is likely wrong
or revoked. Open the API keys page in your OpenAI account, create a fresh key, and
paste it into .env with no surrounding quotes. Never wrap the value in quotes in
a .env file; python-dotenv keeps them as part of the string.
Fix 4 — Match the organization, project, and provider
A correctly typed, current key can still return 401 if it points at the wrong
place. This happens when your account has more than one organization or project, or
when base_url is aimed at a different provider whose servers reject an OpenAI key.
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
organization=os.environ.get("OPENAI_ORG_ID"), # optional, set only if needed
project=os.environ.get("OPENAI_PROJECT_ID"), # optional, set only if needed
base_url="https://api.openai.com/v1", # the real OpenAI endpoint
)
print("Talking to:", client.base_url)
Confirm base_url reads https://api.openai.com/v1. If you earlier set it to a
free or third-party gateway, an OpenAI key will be rejected there. Mixing providers
is a frequent trap when following tutorials that compare services such as
Groq vs OpenRouter Free Tier;
each provider needs its own matching key. Likewise, only set organization and
project if your key belongs to that exact org and project. A mismatched org or
project ID is just as fatal as a bad key.
Fix 5 — Run a minimal working call
Once the checks pass, prove the fix with the smallest possible request. A clean reply here means your authentication is fully working.
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Reply with the single word: working"}],
)
print(resp.choices[0].message.content)
If this prints working, the 401 is solved. Build the rest of your program on top
of this proven client.
Cause and fix quick reference
| Cause | Fix |
|---|---|
.env never loaded | Call load_dotenv() before reading the key; run from the folder holding .env |
| Wrong variable name | Use OPENAI_API_KEY exactly, in both .env and os.environ |
| Key mistyped or has quotes/spaces | Re-paste cleanly with no quotes; check key.strip() and the sk- prefix |
| Key revoked or expired | Generate a new key in your OpenAI account and update .env |
| Wrong org or project | Set organization and project to match the key, or remove them |
base_url points at another provider | Reset base_url to https://api.openai.com/v1 and use that provider's own key |
Troubleshooting
KeyError: 'OPENAI_API_KEY'— The variable is not in the environment. Cause:load_dotenv()ran from the wrong folder, or the.envline is misspelled. Fix: run the script from the folder containing.env, or load an absolute path withload_dotenv("/full/path/.env").- 401 only when running from cron or a server — The shell that loads
.envdiffers from the one your job uses. Cause: a hardcoded environment variable on the machine overrides.env. Fix: addload_dotenv(override=True)so the file wins, or unset the stale variable withunset OPENAI_API_KEY. - Key works in the browser playground but not in code — You copied a session
token, not an API key, or the wrong account's key. Fix: copy the key from the
API keys page (it starts with
sk-) and confirm you are signed into the right account. - 401 right after it worked yesterday — The key was rotated or revoked, or a free trial expired. Fix: generate a new key and confirm your account has active billing or remaining quota.
Still failing? Final checklist
Work down this list and the 401 almost always falls:
- The key prints a non-zero length and a
sk-prefix at runtime. .envhas no quotes, no trailing spaces, and uses the nameOPENAI_API_KEY.- You ran the script from the folder that holds
.env, or loaded an absolute path. - The key is current: not revoked, not from an expired trial.
base_urlishttps://api.openai.com/v1, not a third-party gateway.- Any
organizationorprojectvalue matches the key, or is left unset. - Your account has billing set up or free quota remaining.
When to use this vs. alternatives
- Use this guide when the status is
401or the SDK raisesAuthenticationError— the cause is always identity, never the prompt or model. - Switch to Fix the 429 Rate-Limit Error in Python
if you see
429; that is throttling or quota, and your key is fine. - Read Fix JSONDecodeError with AI API Responses in Python if the call succeeds but parsing the reply fails; that is a response-shape issue, not authentication. And see Fix the Context-Length-Exceeded Error in Python when the request is too long rather than unauthorized.
Back to Understanding LLM APIs.
Related guides
- Understanding LLM APIs — the main guide for keys, requests, and parameters.
- Fix the 429 Rate-Limit Error in Python — when the issue is throttling, not identity.
- Fix JSONDecodeError with AI API Responses in Python — when the call works but the reply will not parse.
- Fix the Context-Length-Exceeded Error in Python — when the request is simply too long.