All guidesSupabase RLS and keys
On this page

Supabase error

Supabase

Invalid API key

By Ohad Krispin, founder of VibeRaven · LinkedIn

"Invalid API key" with HTTP 401 is Supabase's API gateway rejecting the apikey header: the key is not one this project accepts, such as a key from another project or environment, a mistyped or cut-off key, or a value that is not a key at all. Copy the Project URL and the publishable key (or the legacy anon key) from the same project, under Settings > API Keys, set them under the names your code reads, and redeploy. Do not follow the hint by swapping in the service_role key: it bypasses row level security and must never ship to the browser. We reproduced the error and both hint texts against a live project on 2026-10-25.

curl against a live Supabase project on 2026-10-25: a made-up legacy key and a made-up sb_publishable_ key each get 401 Invalid API key with a different hint, and no key at all gets No API key found in request.
curl against a live Supabase project on 2026-10-25: a made-up legacy key and a made-up sb_publishable_ key each get 401 Invalid API key with a different hint, and no key at all gets No API key found in request.

Repository check

Check this in your repo

Find a service role key headed for the browser

For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. It cannot tell whether a key is valid for your project, because it never sends one. What it does find is the dangerous answer to this error's hint: a service role or secret key under a NEXT_PUBLIC_ or VITE_ name, reported as service_role_key_in_client_env, and a Supabase client created with the service role key in a file that ships to the browser, reported as service_role_key_in_client_code.

It is a repository check, not a live database test: it reads files, so it does not call your project or see which key your deployment holds. It is advice, not a gate, and it exits with code 1 when it finds a blocker.

npx -y viberaven@1.6.12 check

What it printed on a fixture repo

On 2026-10-25 we ran it on a small Next.js fixture repo made for this page, not a customer repo, whose browser client had been switched to process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY, the change the hint invites. The output is below, with the temp folder shortened to ~/notes-app. Both findings are blockers, so the exit code was 1.

$ npx -y viberaven@1.6.12 check

viberaven check · ~/notes-app

🔴 Service role key in a client-prefixed env variable  (service_role_key_in_client_env)
   .env.example:2: NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY names a service role or secret key under the NEXT_PUBLIC_ prefix. NEXT_PUBLIC_* variables are exposed to client code by Next.js, so anyone who loads the app can read it and skip RLS entirely. Rename it without the prefix, read it only in server code, and rotate the key.
🔴 Service role key used in client code  (service_role_key_in_client_code)
   src/lib/supabase.ts:5: creates a Supabase client with the service role key. This file ships to the browser, and the service role key skips RLS entirely. Move the call behind a server route or edge function and rotate the key.
⚪ No error monitoring detected  (missing_monitoring)
   No Sentry/PostHog (or similar) instrumentation was found. Production errors will only surface when users complain.

Verdict: ❌ 2 blockers, 0 warnings · score 55
Fix: viberaven fix · Details: ~/notes-app/.viberaven/agent-tasklist.md

The two hints, and the message with no key

We sent requests to a live Supabase project's REST endpoint on 2026-10-25. A made-up key in the legacy JWT format got the first body below and a made-up sb_publishable_ key got the second, both with HTTP 401, and the Auth endpoint, /auth/v1/settings, answered another made-up key with the first body. A request with no apikey header got the third. supabase-js does not throw: the call returns the message in error.

{"message":"Invalid API key","hint":"Double check your Supabase `anon` or `service_role` API key."}
{"message":"Invalid API key","hint":"Double check the provided API key for typos. This API key might also be owned by another Supabase project."}
{"message":"No API key found in request","hint":"No `apikey` request header or url param was found."}

The URL and the key come from different projects

Each key belongs to one project, which is what the second hint means by "owned by another Supabase project". It happens when the URL and the key come from different places: a staging key with the production URL, the keys a local supabase start stack prints sent to the hosted project, or an old key left in Vercel after the app moved to a new project. Check that the project ref in the URL the deployed app calls is the project you copied the key from.

Publishable and secret keys go in the apikey header

Publishable (sb_publishable_) and secret (sb_secret_) keys are not JWTs. Supabase's API keys guide says to send them on the apikey header, not as Authorization: Bearer, because anything that tries to verify one as a JWT fails. supabase-js does this for you. It also says a secret key does not work in a browser: Supabase matches on the User-Agent header and returns 401.

Invalid API key after switching to the new keys

Creating publishable and secret keys does not revoke the legacy anon and service_role keys; both keep working until someone disables the legacy keys under Settings > API Keys, which Supabase says you can reverse. If requests from one client started failing right after that, it still sends an old eyJ key: update that client, or turn the legacy keys back on while you finish the move. Supabase is deprecating the legacy keys by the end of 2026.

Checklist

How to fix it, then check the fix

  1. 01Open the browser's Network tab on the failing page and note the host the request went to: <project-ref>.supabase.co.
  2. 02In that project, open Settings > API Keys and copy the publishable key, or the legacy anon key if the project still uses it.
  3. 03
    Find the names your code reads for the URL and the key:
    grep -rnE "SUPABASE_(URL|ANON_KEY|PUBLISHABLE_KEY)" --include=*.ts --include=*.tsx --exclude-dir=node_modules .
  4. 04Set both values in Vercel under those exact names, for Production and Preview, then redeploy: a deployment keeps the values it was built with.
  5. 05
    Test the pair outside the app; an Invalid API key body means the project still does not accept the key:
    curl -s "https://<project-ref>.supabase.co/auth/v1/settings" -H "apikey: <publishable key>"
  6. 06Keep the secret or service_role key on the server, even if the error suggests it.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

The key is copied straight from the dashboard. Why is it still invalid?

Check two things: the URL the app calls may belong to another project, or the deployment was built before you changed the variable. Compare the host in the failing request with the project you copied the key from, then redeploy.

Should I use the service_role key, since the hint names it?

Not in the browser. The hint names both legacy key types because either can be the one sent. The service_role and secret keys bypass row level security, so they belong only in server code.