The server received your request, but it could not match the string in the x-api-key header (in the OpenAI-compatible format that is Authorization: Bearer) to any active key. That is "Invalid API key". Anthropic answers with an HTTP 401 and the type authentication_error, and it is not an error in the request body. The model and the prompt have nothing to do with it: the credentials that reached the server are broken. There are four causes, and none of them needs a support ticket.
What the error looks like
The raw server response:
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "invalid x-api-key"
}
}
What you see depends on the tool:
- curl prints that JSON in full, with status code
401. - The Python and Node SDKs wrap it in an authentication exception (
anthropic.AuthenticationErrorin Python), and the same message lands in the traceback. - Claude Code shows the error in the terminal on the first call to the model. The CLI passes the API response through as it is, at best adding a hint to check your environment variables.
- Cursor and other IDEs with a custom API key complain on the first test request after you save the key, usually with an
authentication errorstatus in the connection settings.
The string is the same everywhere: invalid x-api-key. The causes below run from the most common to the rarest.
Cause 1: a typo in the key, or the key was revoked
The most ordinary one. An Anthropic key looks like sk-ant-api03-... and is shown once, when you create it. Copy it only partway or catch an extra character, and the server gets a string that resembles no key at all. The reply is the same as for random letters.
A revoked key is indistinguishable from a wrong one: the error text is identical. Keys are revoked by hand from the console, and a key with an expiry date is revoked on its own once that date passes. Checking is easy: open the key list in the console and see whether the key is there and active. If it is gone, create a new one. The old value cannot be recovered.
Cause 2: the key belongs to another organization
People forget this one most often, because the key looks healthy: it exists, has not expired and was copied without mistakes. But a personal Anthropic key is tied to an organization in the console. If you were removed from that organization, or left it yourself, the key stops authenticating. The same happens when you have several organizations (say, a personal one and a work one): the key was created in one, and you expect the settings of the other. Limits, models and billing differ between them, and a key will not pick up another organization's settings.
There is also a special case. If a key works across several workspaces, the request may need a header with the workspace identifier. Without it the server sometimes cannot tell which workspace the request belongs to and returns the same authentication error. If you administer an organization with several workspaces, check this right after the key itself.
Cause 3: a space or line break in the environment variable
Copying a key from the console takes one motion, but it has plenty of chances to get damaged on the way to the environment variable:
- the terminal appends a line break at the end when you paste;
- a
.envfile has a space after the equals sign; - the key was copied from a document or chat where the text wrapped, and an invisible line break landed in the middle of the string.
Nothing looks wrong to the eye, but the server compares the key character by character, and one extra byte breaks the match. A check in the terminal:
echo -n "$ANTHROPIC_API_KEY" | wc -c
echo -n "$ANTHROPIC_API_KEY" | cat -A | tail -c 20
The first command prints the length of the value. If it is one or two characters longer than expected, a stray space or \n is sitting somewhere, and in the cat -A output it shows up as a $ in an unexpected place. A more reliable route is to delete the variable and set it again, typing export by hand and pasting the key with no quotes around it.
Cause 4: the key does not match the server address
An Anthropic key sk-ant-... and a codexkey key sk_cdx_... are meant for different base addresses. Send sk_cdx_... to api.anthropic.com and Anthropic does not recognize its own format, so you get authentication_error. The reverse fails too: an sk-ant-... key sent to https://api.codexkey.online is rejected by the gateway, which expects a key of its own format.
The mix-up shows up when you move from one service to another. You changed base_url in one place in the configuration, but the old key stayed in an environment variable or in the IDE settings, or the other way around. So check the pair "key and address" as a whole.
How to find the cause quickly
An order that covers all four cases in a couple of minutes:
- Look at the first 10-12 characters of the key:
sk-ant-means Anthropic,sk_cdx_means codexkey. Compare that with thebase_urlit is sent to. - Measure the length of the environment variable with the command above to rule out a space or a line break.
- Open the key list (in the Anthropic console or in your codexkey account) and make sure the key is there, active, and created in the right organization or workspace.
- If everything matches and the error stays, issue a new key and replace the old one. Sometimes that is faster than hunting for the exact spot where the old value got damaged.
If the problem is the Anthropic key itself
Suppose the checks show no typos and no environment problems: the Anthropic key is revoked, the organization closed your access, or you simply do not want to depend on a single key. Then you can reach the Claude models through a separate codexkey key that has nothing to do with the earlier account.
The key is created in your account, in the "API keys" section, and looks like sk_cdx_.... For direct API calls and SDKs the base address is https://api.codexkey.online.
import anthropic
client = anthropic.Anthropic(
api_key="sk_cdx_YOUR_KEY",
base_url="https://api.codexkey.online",
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)
Claude Code is configured with two environment variables, and you do not need to sign in to the Anthropic console for it. The steps for Linux, macOS and Windows are in Claude Code in Russia. The other API error codes (429, 500, 529) are collected in the Claude API errors guide. For current models and prices see the models page.
FAQ
Why does curl return "invalid x-api-key" when I copied the key correctly?
Most often invisible characters are to blame: a line break at the end of the value, or a space after the equals sign in .env. Measure the length with echo -n "$ANTHROPIC_API_KEY" | wc -c and compare it with the expected key length.
Can a key stop working on its own, without anything I did?
Yes. A personal Anthropic key is tied to an organization in the console and stops authenticating if you are removed from that organization. Keys with an expiry date lapse automatically, on the schedule set when they were created.
How is 401 different from 403, if both errors are about access?
A 401 authentication_error means the key itself is not recognized as valid. A 403 permission_error means the key works, but it has no rights to the specific resource or model: for example, the model is disabled for your plan, or the key is limited to a list of allowed models.
Can I use a codexkey key in place of an Anthropic key, and the other way around?
No. The formats differ (sk_cdx_... against sk-ant-...), and each key works only with its own base address: the codexkey key with https://api.codexkey.online, the Anthropic key with api.anthropic.com. When you switch, replacing one line in the code, base_url, is usually enough.
Do I need to change the request header when I move to codexkey?
No, if you use the Anthropic SDK or a compatible format: the header stays x-api-key, and only the key value and base_url change. In the OpenAI-compatible format the header is Authorization: Bearer, and the same rule applies.
The error appeared suddenly, but everything worked yesterday. What changed?
Start with the key console: the key may have been revoked by hand, may have reached its expiry date, or the organization's membership may have changed. If the key itself is fine but the environment has changed since yesterday (reinstalled dependencies, an edited .env, a different terminal), suspect a stray character that got into the variable during copying.