Invalid API Key OpenAI: Fix the 401 Error
8 min read · 1,486 wordsBogdan KolomietsBogdan Kolomiets

Invalid API Key OpenAI: Fix the 401 Error

TL;DR

Getting a 401 invalid_api_key from OpenAI? Here is the fast checklist, the real causes, a curl test, and how 401 differs from 403 and 429.

Your code worked yesterday. Today every call returns a `401` and a message about an incorrect API key. Your prompt, your model choice and your request body are all innocent — the server rejected you before it looked at any of them.

Here is the order I work through it in: the cause behind most of these failures first, the rare ones last.

Start here: the 60-second checklist

Run these five checks before you touch a line of code.

1. Open your API keys page, confirm the key still exists, and look at its "last used" date. 2. Compare the **length** of the string in your app with the length in the dashboard — the length, not the characters. A truncated paste looks identical to the eye. 3. Check for a trailing space or newline at the end of the value. 4. Restart the process. A rotated key changes nothing while the old value is still in memory. 5. Confirm the key belongs to the project and organization the request runs under.

Most of the 401s I have chased came down to items 2 and 4.

What "invalid_api_key" actually means

The `401` family is about authentication: the server checks *who* is calling before it looks at *what* was asked. OpenAI's help article on this error is blunt about the remedy — verify the key against the dashboard, and make sure you are not mixing two different keys inside one application.

The status code alone cannot diagnose it, because several unrelated situations share it. The detail lives in the response body:

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

Read `error.message`, not the number. That field separates "this string is wrong" from "your account is not in an organization." Many client libraries and no-code nodes surface only the status code, so you often have to dig the message out of the raw response yourself.

OpenAI's error reference lists four causes under `401`: *Invalid Authentication*, *Incorrect API key provided*, *You must be a member of an organization to use the API*, and *IP not authorized*. Each needs a different fix.

Test the key with one curl request

Before debugging your application, prove the credential itself is alive:

curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

A `200` with a list of models means the key is fine and the bug is in your app — usually in how it loads the value. Another `401` means the problem is the string or the account behind it, and rewriting your client will not help.

If the command succeeds in your terminal but the app still fails, the app is reading a different value than your shell. And if the variable is empty, curl sends an `Authorization` header with nothing after `Bearer`, producing the same `401` — so check the variable's length before blaming the code.

The string itself: typos, truncation, and revoked keys

This is the most common cause by a wide margin. Keys break in transit — copied out of a chat client that wrapped the line, pasted into a field that trimmed at a fixed width, saved to a `.env` file with quotes the loader never stripped.

You will not catch this by staring at it. Compare lengths. And do not print the key to a log to "check it": logs outlive the incident and get read by more people than you expect. Print the length and the last four characters instead.

One thing that is *not* a defect: project-scoped keys beginning with `sk-proj-` are longer than the older format. Forum threads regularly open with someone assuming a fresh key is malformed because it is longer than the one it replaced. That length is normal.

A revoked key stops working instantly, and your application is never told. The last-used column shows whether the key you are editing is the one your app actually sends. Caching is the sneaky variant: you rotate the key, update the config, and the failure persists because the old value still sits in a running process, a warm serverless container, or a build artifact. Restart and redeploy. Deleted keys cannot be recovered — issue a new one and replace it everywhere.

Wrong project or organization

If your account belongs to more than one organization, a key issued in one will not authenticate a request made in the context of another. That is usually what the *Invalid Authentication* variant means: OpenAI's guidance is to check that both the key **and** the requesting organization are correct.

Current keys are scoped to a project, and a project key is limited to the models and resources that project permits. When anything is ambiguous, set the identifiers explicitly rather than relying on defaults:

curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Organization: org-..." \
  -H "OpenAI-Project: proj_..."

A related variant — *You must be a member of an organization to use the API* — means the account is not attached to an organization at all. Only an owner can fix that, by inviting you.

Your application never received the key

The credential is valid, but never reaches the request. Usual suspects:

  • The `.env` file sits next to the code, but the loader is never called — or it runs *after* the client is constructed.
  • The container was rebuilt from a stale environment, so the variable in the image is not the one you edited.
  • A system-level variable shadows the one in your file.
  • The name is misspelled: `OPENAI_KEY` instead of `OPENAI_API_KEY`. The SDK finds nothing under its default variable and sends an empty credential.
  • The client points at a proxy or gateway `base_url` while the key was issued by OpenAI directly, or the reverse.

Check your SDK version too — very old clients still target auth patterns and endpoints that have since changed.

In the Python SDK this surfaces as `AuthenticationError`. Catch it separately: unlike `429` and `500`, it will never succeed on a retry, so wrapping it in retry logic only burns time.

When it is not a 401 at all

Three codes get confused constantly, and each needs a different response.

| Code | Message | Meaning | What to do | | --- | --- | --- | --- | | 401 | Incorrect API key provided | the string is not recognised | verify it, or generate a new one | | 401 | IP not authorized | request IP is outside the allowlist | send from an allowed IP, or update the allowlist | | 403 | Country, region, or territory not supported | request came from an unsupported location | not fixable in application settings | | 429 | Rate limit reached for requests | requests arriving too quickly | slow down and honour `Retry-After` | | 429 | Credit balance exhausted | the organization has no prepaid credits left | add credits |

The short version: `401` means we do not recognise you, `403` means we recognise you but you may not be here, `429` means we would let you through, just not right now. A new key only helps in the first case. Note that API access is unavailable in a number of countries and regions — that returns `403`, not `401`.

Storing and rotating keys without repeating this

Most repeat incidents come from how the key is kept, not the key itself.

Keep the value in environment variables or a secrets manager, never in the repository — and add `.env` to `.gitignore` before the first commit, not after. Issue a separate key per application: one shared key means a single revocation takes down every integration, and spend cannot be attributed to anything. Set a project spend limit so a runaway loop is capped by policy rather than by your invoice. Keys have no expiry date, which is why forgotten ones are dangerous — audit the list and delete anything with no recent activity.

If a key leaks, the order is fixed: create the replacement, deploy it, confirm traffic is flowing, then revoke the old one. Reversing those steps guarantees downtime.

There is also the case where you need not manage keys at all. If the goal is publishing content rather than building on the model, our [SEO article generator](https://ai-seowriter.ru/en/generator-seo-statej) runs on the service's own keys, and you can connect your own later if you prefer.

Frequently asked questions

**The key looks correct, but the error keeps coming back.** Compare its length in your app with the length in the dashboard, and confirm the process restarted after you replaced it.

**Do OpenAI API keys expire on their own?** No. A key works until it is revoked — which is why abandoned keys are a liability.

**Does a ChatGPT subscription give me API access?** No — two separate balances in one account.

**Will switching models fix it?** No. The rejection happens during authentication, before the model is considered.

Automate SEO publishing with SEO Writer

AI writes articles, publishes to CMS, fills meta tags — without your involvement

Start for free →