Invalid API key is the refusal a service sends when the credential your app handed over is wrong, expired, rotated, restricted or from another project. The wording names the vendor: OpenAI says Incorrect API key provided with a 401, Stripe says No valid API key provided. Read that line before you replace anything.
What does invalid API key mean? The words each service returns
An invalid API key error means the service your app called checked the credential it was handed and rejected it. The key is wrong, expired, rotated, restricted or from another project. The refusal comes from the service, not from your app, and its exact words name which service it was.
Every service your app talks to hands out a key, and your app attaches that key to each request it sends. The service checks the key before it does anything and answers; the error on your screen is that check failing. So the fault sits between your app and one outside service, which is why one feature can stop while the others keep working. If you want the background first, what an API key is covers the term itself and how a key differs from a secret or a token.
Each row below pairs the words of an invalid API key refusal with the vendor’s own meaning, copied from each vendor’s own docs on 21 October 2026.
| Service | The words you see | Status | What the vendor says it means |
|---|---|---|---|
| OpenAI | Incorrect API key provided | 401 | ”The requesting API key is not correct.” |
| OpenAI | Invalid Authentication | 401 | Fix given: “Ensure the correct API key and requesting organization are being used.” |
| OpenAI | credit_balance_exhausted | 429 | ”Your organization has no prepaid credits remaining.” |
| OpenAI | organization_spend_limit_exceeded | 429 | ”Your organization reached its enforced spend limit.” |
| OpenAI | project_spend_limit_exceeded | 429 | ”Your project reached its enforced spend limit.” |
| Anthropic | authentication_error | 401 | ”There’s an issue with your API key (for example, it’s malformed, revoked, or expired …)” |
| Stripe | No valid API key provided | 401 | Stripe’s whole description of its 401 status |
| Stripe | 403 Forbidden | 403 | ”The API key doesn’t have permissions to perform the request.” |
| Stripe | api_key_expired | not stated in Stripe’s table | ”The API key provided has expired.” |
| Stripe | secret_key_required | not stated in Stripe’s table | ”The API key provided is a publishable key, but a secret key is required.” |
| Stripe | livemode_mismatch | not stated in Stripe’s table | ”Test and live mode API keys, requests, and objects are only available within the corresponding mode.” |
| Supabase | UNAUTHORIZED_MISSING_API_KEY | gateway code in sb_error_code | ”the error code the API gateway recorded for a request” |
| Supabase | no_authorization | not stated | ”This HTTP request requires an Authorization header, which is not provided.” |
| Supabase | bad_jwt | not stated | ”JWT sent in the Authorization header is not valid.” |
| Google Maps | InvalidKeyMapError | browser console | ”The API key included in the script element that loads the API is not found.” |
| Google Maps | ExpiredKeyMapError | browser console | ”… has expired or is not recognized by the system.” |
| Google Maps | ApiNotActivatedMapError | browser console | ”The Maps JavaScript API is not activated on your API project.” |
| Google Maps | RefererNotAllowedMapError | browser console | ”The current URL loading the Maps JavaScript API has not been added to the list of allowed referrers.” |
| Google Maps | BillingNotEnabledMapError | browser console | ”You have not enabled billing on your project which is causing this error.” |
| Resend | missing_api_key | 401 | ”Missing API Key” |
| Resend | validation_error: API key is invalid | 401 when sending | Returned “for a key that doesn’t exist, was deleted, or isn’t active.” |
| Resend | restricted_api_key | 403 | ”API key is not active” |
| Resend | suspended_api_key | 403 | ”This API key is suspended” |
| Resend | validation_error: domain not verified | 403 | ”The domain.com domain is not verified. Please, add and verify your domain.” |
| SendGrid | authorization required | 401 | The message in SendGrid’s unauthorized response body |
The rows come from OpenAI’s error-code guide, Anthropic’s API errors page, Stripe’s errors reference and Stripe’s error-code table, Supabase’s REST error codes and its Auth error codes, Google’s Maps JavaScript error list, Resend’s error table and SendGrid’s Mail Send reference, where the 401 body reads {"errors":[{"message":"authorization required","field":"null"}]}.
Two gaps matter as much as the rows. Stripe’s two error pages and Supabase’s error and API key pages do not print the four words Invalid API key anywhere, and Resend’s closest message is API key is invalid. So when those four words sit beside a Stripe or Supabase failure, the documented codes above are what to match, and the raw response in the browser’s network tab or the function log tells you which code it was. If your screen says invalid API key provided rather than OpenAI’s Incorrect API key provided, go by the status number; the three reads further down explain what each number means.
Why is my API key not working when it worked yesterday?
A key that worked yesterday stops for one of four reasons, none of which needs a code change: the key was rotated or revoked, the account ran out of money, the app runs somewhere the key is not valid, or a test key is doing live work.
A key was rotated or revoked
Someone generated a new key and the old one stopped, either at the vendor or through a security tool doing its job. Supabase’s version reaches furthest. Supabase’s guide to rotating secrets says that “Once the JWT secret is regenerated, all current API secrets will be immediately invalidated, and all connections using them will be severed”, and that “You will need to deploy the new secrets for connections to begin working again.” Saving the new value in one place is half the job; every copy still in use keeps failing until it is replaced.
Stripe’s api_key_expired says the same thing in calmer words, and its dashboard gives a cushion: when you rotate a key there, both the old and new keys work for up to 7 days. A key rotated last week can therefore fail today, once that window closes, with nobody having touched anything since.
Supabase is also changing its key formats. Publishable keys (sb_publishable_...) and secret keys (sb_secret_...) replace the older anon and service_role keys, which Supabase is deprecating “by the end of 2026”; the older keys stay valid until you disable them in the dashboard. The steps for changing a key without an outage are in how to rotate API keys safely.
Some rotations start with a leak. We audited 21 third-party apps in June and July 2026, and 6 of them shipped a real secret. Those apps were picked for audit rather than drawn at random, so the count describes that group and not AI-built apps in general. A key that leaks gets revoked, and every copy of it still wired into the app then reads as invalid. If a key may have leaked rather than expired, the first hour is set out in what to do when an API key leaked.
If one rotation broke several connected features at once, or the app is live with paying users and nobody knows which services depend on which key, start with a free audit: a 30-minute video call where we go over the failing features with you and agree the order to bring them back. In the Production Hardening Sprint, our engineers document how to rotate each key, update its dependents, verify the change, and recover from a failed rotation, and move private AI, email, SMS, and other service keys server-side and implement usage caps.
The balance ran out or a spend cap was hit
OpenAI codes an empty balance as a 429, not a 401: credit_balance_exhausted, “Your organization has no prepaid credits remaining.” Its spend caps return the same status, as organization_spend_limit_exceeded and project_spend_limit_exceeded. In each case the key is valid and there is simply nothing left to spend, so a fresh key changes nothing. OpenAI’s guide is blunt about it: “Retrying billing, spend, or quota errors won’t restore API access.”
A cap set months ago, at much lower usage, can stop a working app on an ordinary afternoon. How to set caps and alerts that warn you first is covered under a monthly cap on a metered API. If it is the builder itself that stopped for lack of its own credits, that is a different failure, described in an app paused for credits.
The wrong environment: the browser, the preview, or a static deploy
A real key can still be refused because of where it runs. Supabase documents one case plainly: “A secret key doesn’t work in a browser. Supabase matches on the User-Agent header and returns HTTP 401 Unauthorized.” The details are in Supabase’s API keys guide.
Replit has two versions of the same trap. Its secrets “are available for all deployment types except Static Deployments”, and a published project reads its own Production app secrets, found under Publishing, then Adjust settings, which can hold different credentials from the workspace. The wider pattern is covered in values that differ between preview and production and in an app that works locally only. If the value is missing rather than refused, start with how environment variables reach a deploy.
A test key where a live key is required
Stripe names both halves of this mistake, in the two rows of the vendor table above. secret_key_required is the code for a publishable key sent where the call needs a secret one, and livemode_mismatch is the code for a test key, request or object used in live mode, or the reverse, since each stays inside its own mode. The prefix tells you which you have: sandbox secret keys start with sk_test_ and live ones with sk_live_, and each mode has its own set of keys. Moving all of Stripe to live involves more than the key, and the Stripe go-live checklist covers the rest; here only the key is in question.
Which of your keys is being refused: three reads before you change anything
A refused key shows its cause in the status code. At OpenAI, a 401 means the credentials were rejected and a 429 with a billing code means the account has no credit left or hit a spend limit; at Stripe and Resend, a 403 means the key is real but the request is not allowed.
An app built with Lovable, Replit or Base44 can hold several keys at once: AI, database, payments, maps, email. Three reads, in this order, narrow them to one.
- 01 Copy the exact string, all of it, including any code written in capitals or inside backticks. A vendor-shaped code such as InvalidKeyMapError or credit_balance_exhausted names the service by itself.
- 02 Find the status number. 401: the key was refused. 403: the key is real but the request is not allowed, often because the key is restricted or switched off. 429 with a billing code: the key was accepted and the account has nothing left to spend. Only the first is a key problem.
- 03 Name the feature that stopped. A blank map beside a working checkout points at the maps key. A screen where nothing loads at all points at the database key, since most of the page reads from it.
Not every vendor uses that split. Anthropic’s API returns a 402 for billing problems and a 400 when usage reaches a spend limit you set (a limit on the Claude Code workspace can return a 429 instead), so read the code name as well as the number. In JavaScript or Python code, an invalid API key reaches you as an exception from the vendor’s library: OpenAI’s libraries raise AuthenticationError for a 401, PermissionDeniedError for a 403 and RateLimitError for a 429, and the Python guide describes the first as a key or token that was “invalid, expired, or revoked.” Maps errors print in the browser’s JavaScript console in the form Google Maps API error: [ERROR CODE].
Before you change any key, rule out an outage at the builder, as described in when Lovable itself is down. A request that never gets an answer is a separate failure from a refused key, explained in an OpenAI request that times out.
Other wordings of the same refusal
Some services phrase the refusal in their own words. These are the ones a vendor page shows today.
| The words | Service that returns them | What the vendor says it means |
|---|---|---|
You have not supplied a valid API Access Key. | apilayer’s APIs, such as VATlayer, which take the key as an access_key parameter on the request URL | apilayer’s example error “triggered when the user did not provide a valid API Access Key”, with the type invalid_access_key |
API key not valid. Please pass a valid API key. | Gemini API and Firebase AI Logic (Google), status 400 | It usually means the key in your Firebase configuration “doesn’t exist or isn’t setup to be used with your app and/or Firebase project” |
keyInvalid | Google’s standard API errors, status 400 | ”The API key provided in the request is invalid” |
API key is invalid | Resend, status 401 on sending | The same Resend row as in the vendor table: an unknown, deleted or inactive key |
Google’s standard error list pairs keyInvalid with a pointer: use the Google Developers Console to find your API key or obtain one, since an invalid key leaves the server unable to check your quota. A message that is in neither table is best matched by its status number, not by guessing which vendor wrote it. A complaint about body keys is a different problem again: as we read it, it names field names in the JSON you sent, not your API key.
This turn failed: API key is invalid, inside an AI coding tool
When the refusal shows up in the chat of an AI coding tool rather than in your app, the key being refused is the one the tool uses to call a model, not one of your app’s keys. Neither Cursor’s API key help nor Claude Code’s error reference prints those exact words, so go by the tool you were using.
In Cursor, your own key sits under Cursor Settings, then Models, beside its provider, and Cursor’s API key help says that “If a key is invalid or rejected by the provider, requests using that provider fail until you update or remove the key.” Claude Code uses an ANTHROPIC_API_KEY from the environment instead of your Claude subscription when one is set (in interactive mode, once you approve it), and its error reference says Invalid API key · Fix external API key means that variable, or an apiKeyHelper script, “returned a key the API rejected”, or Claude Code blocked the key before sending it. Its advice: “Check for typos and confirm the key has not been revoked in the Console”. Its Credit balance is too low message can mean the Console organization ran out of prepaid credits, or that Claude Code is sending requests with a Console API key when you meant to use your subscription.
The split runs both ways. Replacing the tool’s key does nothing for the app, and the app’s keys have no effect on the tool.
Where Lovable, Replit and Base44 keep your API keys
Lovable, Replit and Base44 each keep API keys in a Secrets store, in different places and with different rules. Lovable’s saved values are write-only, Replit splits project secrets from account and production secrets, and Base44 also saves a key pasted into its chat. The fix starts in the right store.
| Builder | Where keys live | Can you read a saved value back? | The exception |
|---|---|---|---|
| Lovable | More, then Cloud, then Secrets | No: secrets are write-only | SUPABASE_ and LOVABLE_ names are managed by the built-in backend; an external Supabase keeps its secrets in its own dashboard |
| Replit | Tools, then Secrets under Setup; Account Settings for Account Secrets; Publishing, then Adjust settings for Production app secrets | Yes: the eye icon next to a secret shows its value (in an organization, Owner role only) | Static Deployments get no secrets |
| Base44 | Dashboard, then Secrets (shown once the app has backend functions) | No: the full value stays hidden after saving | Custom OpenAPI integrations keep credentials as workspace secrets; the published app keeps the values from its last publish |
Lovable API key: Secrets, write-only values, and the built-in backend
Lovable’s Secrets documentation gives the path as More, then Cloud, then Secrets in the project toolbar, and says the values are “encrypted, injected into your Edge Functions automatically, and never reach the browser.” The rule that matters most when a key is refused: “Secrets are write-only: after you save a secret, its value can never be viewed again in Lovable, only replaced or deleted.” You cannot compare the saved value with the vendor’s, so pasting a fresh copy from the vendor is the correct check, not a guess.
Names that start with SUPABASE_ or LOVABLE_ “are managed automatically by the built-in backend and cannot be created or overwritten in the Secrets UI”. The built-in backend runs on Supabase’s open-source foundation, which is not the same as a Supabase project you connect yourself; on an external project, the backend view’s Manage secrets button opens that project’s Supabase dashboard, and the keys live there.
Lovable AI needs no key of yours: Lovable’s integrations docs say it “gives your app AI capabilities without API keys or external accounts.” So a refused Lovable API key for OpenAI or another AI provider was added by someone on purpose, and it is that account’s balance and key you check. Which integration failed, service by service, is covered in which Lovable integration broke.
Replit: project secrets, account secrets and production secrets
Replit’s Secrets documentation describes a tool that “stores and encrypts secrets, your Replit project’s sensitive information”, opened from Tools, then Secrets under Setup. Your code reads each one as an environment variable, so the name in the pane and the name in the code have to match exactly; a key saved under a slightly different name never reaches the code at all.
Account Secrets live in Account Settings and reach a project only once linked with the Link Account Secrets button. The published project reads Production app secrets, under Publishing, then Adjust settings, and Replit’s own advice is to review those values, “especially if you use different credentials for testing and production.” A Static Deployment receives none of them.
Base44: Secrets, keys typed into the chat, and workspace integrations
Base44’s integrations documentation puts app secrets under Dashboard, then Secrets, a page that appears once the app has backend functions, which need a Builder plan or higher. A key pasted into the AI chat when it asks for one is “saved as a secret for you”, so a key typed into a chat weeks ago lives in the same list. Backend functions find each secret by its exact name, and after saving, “its full value stays hidden, even from you.”
Two exceptions change where you look. Custom OpenAPI integrations keep their credentials as “encrypted workspace secrets, not per-app Secrets”, set by a workspace admin, so an app owner will not find them in the app. And “Your published app keeps the values it had when you last published, so publish your app again to make your live app use the change.” Base44’s preinstalled integrations mostly need “no extra setup or separate API keys”, so a failure there is not a key of yours.
Where a private key should live so the browser never sees it
Some keys are made to be public. Stripe’s publishable key can go “in front-end code or applications you distribute”, Supabase’s publishable key is “Safe to expose online”, and a Google Maps key sits in the page and can be held to a list of allowed referrers. Others must never leave the server: Stripe says “Only publishable keys are safe to expose outside your application’s backend”, and Supabase’s secret keys are for backend components only, and they bypass Row Level Security.
AI and email keys belong on that private side too. A private key shipped to the browser can be read by anyone who opens the page source, and the fix is a server-side function that holds the key and makes the call for the page. Which keys can sit in front-end code, case by case, is covered in which keys can live in the browser, and the whole area is mapped in secrets management for an AI-built app.
How to fix an API key error: the seven-step check
An API key error is fixed in seven steps, in a fixed order: match the exact string, read the status, open the builder’s store, check for a recent rotation, check the balance, confirm which copy of the app you are testing, then paste a fresh key and test again.
To fix an invalid API key without guessing, work through the steps below in order and stop at the one that explains the error.
- 01 Find the exact string in the vendor table near the top of this page, or in the table of other wordings.
- 02 Read the status. A 401 means go on to step three. A 429 with a billing code is a billing question, not a key question. A 403 means the request was refused with a real key: a restricted, suspended or inactive key, or a sending domain that is not verified.
- 03 Open the one place your builder keeps that key: Secrets in Lovable, the Secrets tool in Replit, Dashboard then Secrets in Base44, or your own Supabase dashboard for an external project.
- 04 Check the vendor dashboard for a key created, rotated or expired in the last few days.
- 05 Check the account balance and any spend cap on the same vendor account.
- 06 Confirm which copy of the app you are testing, the preview or the published one, and which set of values that copy reads.
- 07 Paste a fresh copy from the vendor, with no space, line break or quote marks around it, under the exact name the code reads. Save, publish or redeploy where the builder needs it, and reload the failing feature.
Keep three things as you go: the original error with its status, the date together with the created date of the vendor’s current key, and the first successful response after the change. A line break inside a pasted key is a real failure mode: Claude Code, for one, stops a request whose key contains a character an HTTP header cannot carry. Base44’s live app keeps the values from its last publish, and Replit’s published project reads its own Production app secrets, which is why step 7 includes publishing.
If a fresh, valid key still fails, the fault was never the key. The builder fix ladders start further back, at the Lovable fix ladder and the Replit fix ladder.
The sprint verifies its secret rotation runbook, deliverable 2.6, this way: rehearse rotation with a test credential and record the affected services and checks. Applied to every key ahead of time, the seven steps above become that rehearsal: you learn which services each key feeds before a rotation shows you.
Common questions about API key errors
How to get a valid API key?
Copy it from the vendor’s own dashboard while signed in to the account that pays for the service, not from a tutorial, an old message or another project. Stripe’s API keys page is where keys are created, revealed, expired and rotated, and each mode has its own set, so take the key for the mode the app runs in. When you create a live secret key, Stripe shows it one time before you save it, so copy it then.
How do I set my API key?
Save it in your builder’s secret store or your host’s environment variables, under the exact name the code reads, then publish or redeploy where the host needs it. Lovable injects secrets into Edge Functions, Replit hands them to code as environment variables, and Base44 needs a fresh publish before the live app uses a changed value. Never put a private key in the page’s source.
What should my API key look like?
An API key is a long string, and at Stripe and Supabase its prefix names its kind and mode. Stripe’s sandbox keys start with pk_test_, rk_test_ or sk_test_ and its live keys with pk_live_, rk_live_ or sk_live_; Supabase’s newer keys start with sb_publishable_ or sb_secret_. A key of the wrong kind is refused even when the value is real, as Stripe’s secret_key_required shows.
When should a founder get help with a key error that keeps coming back?
Get help when a fresh key from the vendor still fails, when one rotation broke several features at once, or when nobody can say which services use which key, because at that point the fault is no longer one value to paste again. At AxonBuild we weigh up with you, in a free audit, which failure to fix first and how to keep the next rotation from breaking anything.
A key error broke a feature that worked yesterday?
Apps built with AI often depend on several outside services, and when one refuses a key, a payment, map or email feature just stops. During the audit, we talk through with you which services your app relies on and where each key lives. You come away knowing which key to fix now and how to stop it happening again.
Book a free audit →
Free · 30 minutes · No code access needed