OpenAI invalid_api_key: meaning, cause, and fix

The cheapest bug in the API, and the most embarrassing: the request died at the door, before a model, a token, or a cent got involved.

By the benchr team · · Verified against OpenAI's error documentation, June 12, 2026

OpenAIHTTP 401severity: highauthentication

Where keys break in real life

Keys rarely fail in the code that uses them. They fail on the way there: a copy that grabbed half the string, an .env entry whose quotes became part of the value, a CI secret saved with a trailing newline, a deploy that shipped staging's key to production.

The other family is administrative. The key was revoked or rotated, it belongs to a different project than the one you're calling, or your server's IP isn't on the allowlist your org enforces. Every one of these returns the same 401 — which is why you diagnose by layer instead of by guess.

The response

HTTP/1.1 401 Unauthorized

{
  "error": {
    "message": "Incorrect API key provided: sk-abc***. You can find your API key in your dashboard.",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}

That body is representative, not a transcript. The masked fragment echoes what you sent: if it doesn't match the key you meant to load, the environment is the bug. Worth knowing: a 401 can also mean missing org membership or an IP outside the allowlist, and a 403 reading "Country, region, or territory not supported" is geography, not authentication.

Ninety-second diagnosis

# 1. Is the env var what you think it is?
echo "len=${#OPENAI_API_KEY} head=${OPENAI_API_KEY:0:4} tail=${OPENAI_API_KEY: -4}"

# 2. Does the key itself pass auth?
curl -s https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

Read the echo first. Length zero means the variable never loaded. A tail showing a quote or odd character means the env file is malformed. If the echo looks right and the curl returns a model list, the key works and your app is loading something else, usually a stale process. If the curl 401s on a key minted minutes ago, stop staring at the string and check project scope, org membership, and the allowlist.

Keep it from recurring

Three habits retire this bug. Keep keys in a secrets manager, not in files that travel with the repo. Issue one key per environment, so a staging leak can't touch production. And run a startup self-check, one cheap authenticated call, so a dead key fails the deploy, not the first customer request. With auth boring again, the interesting question is spend: GPT-5 runs $1.25 in and $10 out per million tokens, and the rankings show what else that budget buys.

Frequently asked

I generated a new key and it still 401s. Why?

Most often the running process never picked up the new value: a stale shell, container, or deploy secret is still exporting the old key. Restart after rotating, confirm the key belongs to the project you're calling, and re-paste if the string may have lost characters.

What's the difference between a 401 and a 403?

A 401 means authentication failed: bad key, missing organization membership, or an IP outside the allowlist. A 403 with "Country, region, or territory not supported" means OpenAI is declining the request's location; a valid key can't fix it.

Is it ever okay to hardcode the API key?

No. Hardcoded keys get committed, forked, and scraped. Keep keys in environment variables at minimum, a secrets manager in production, and issue separate keys per environment so one leak stays contained.

Changelog

  • — Published. Status code, message prefix, and the 401-versus-403 split confirmed against OpenAI's error-codes guide.

Sources

  • OpenAI error codes guide: developers.openai.com/api/docs/guides/error-codes (verified June 12, 2026)
  • benchr api-errors.json: the structured record this page is built from