Stripe API Keys: Test vs Live, Secret vs Publishable (2026)

Stripe publishable keys (pk_) are safe in your app. Secret (sk_), restricted (rk_) and organization (sk_org_) keys must stay on your server. Each has a test and a live version.

Gary 路 Published 路 Updated 路 16 min read 路 Tested on our demo app on

Isometric illustration of a glowing open key card beside a browser window and a sealed server vault holding locked key cards, with emerald light on a dark slate background.
On this page
  1. What are Stripe API keys?
  2. Every Stripe key type at a glance
  3. How do I find my Stripe API key?
  4. What's the difference between Stripe test keys and live keys?
  5. Should you use a Stripe restricted API key instead of a secret key?
  6. Is the Stripe webhook secret an API key?
  7. We scanned a page with fake Stripe keys: here's what got flagged
  8. Leaked a Stripe secret key? Do this now
  9. FAQ
  10. Check your app before you take payments

Stripe API keys come in two families. Publishable keys (pk_) are made to sit in your app. Secret, restricted and organization keys (sk_, rk_, sk_org_) must stay on your server. Each comes in a test and a live version. If you are wiring up payments in an app built with an AI tool, this guide shows which key goes where, and what our scanner caught when we hid fake keys in a demo page.

By Gary, Greenlight My App 路 Updated Oct 27, 2026 路 How we checked this: on Oct 27, 2026 (6:50 to 6:58 AM ET) we ran the Greenlight My App scanner against a local demo page on our own machine, once with all eight Stripe key types and once per type. Every key was a fake placeholder (the prefix plus a row of X's). No real keys, no Stripe account and no requests to Stripe. Stripe's public docs were read the same morning. Drafted with AI tools and reviewed by a person before publishing.

What are Stripe API keys?

Stripe API keys are the strings your app sends to prove which Stripe account is calling. Publishable keys (pk_) can live in a browser or mobile app. Secret, restricted and organization keys must stay on a server. Every key has a sandbox (test) version and a live version, so your app has several keys in total.

Think of a Stripe API key as a name tag plus a permission slip. The prefix tells you both what kind of key it is and whether it works with real money. If you are new to the general idea, our guide to what an API key is (external) covers public versus secret keys for any service. This post stays on Stripe.

Here is the short rule from Stripe's API keys docs: "Only publishable keys are safe to expose outside your application's backend." Everything below is about applying that one sentence.

Every Stripe key type at a glance

Stripe has four API key types, plus a webhook signing secret that people often mix up with them. Only the publishable key is safe to expose. A Stripe secret key, a restricted key and an organization key can all move money or read account data, so they belong in server environment variables or a secrets vault.

PrefixWhat it isWhere it livesSafe to expose?
pk_test_ / pk_live_Publishable key. Used by Stripe.js, Elements and mobile SDKs to create tokens or payment methods. Can't create charges or read account data.Browser or mobile appYes
sk_test_ / sk_live_Secret key. Unrestricted access to Stripe's APIs.Server onlyNo
rk_test_ / rk_live_Restricted key. Only the permissions you pick (None, Read or Write per resource).Server onlyNo
sk_org_Organization key. Like a secret or restricted key, but works across the accounts in a Stripe organization.Server onlyNo
whsec_Webhook signing secret. Not an API key: it verifies that webhook events really came from Stripe.Server (your webhook endpoint)No

Source: Stripe docs, API keys, restricted API keys and webhooks pages, checked Oct 27, 2026.

Stripe's API keys docs page showing the key types table with Publishable key pk_ marked safe to expose and Restricted key rk_ marked not safe to expose Stripe's docs list each key type and whether it is safe to expose. Screenshot of docs.stripe.com/keys, Oct 27, 2026.

So the stripe publishable key is the one you paste into frontend code, and the stripe secret key is the one that must never get there. Stripe also mentions managed API keys, which some hosting platforms issue and rotate for you; you don't handle those directly.

Diagram: the browser or app bundle holds only pk_test_ or pk_live_ keys; your server environment holds rk_live_ (preferred) or sk_live_ and sk_org_; the webhook endpoint holds whsec_ and verifies the Stripe-Signature header; secret keys are marked never in the bundle Where each Stripe key belongs. Original diagram by Greenlight My App, based on Stripe's docs (Oct 27, 2026).

How do I find my Stripe API key?

To find your Stripe API key, open the API keys page in the Stripe Dashboard. Stripe's docs say that page is where you create, reveal, expire and rotate keys for the account or sandbox you are viewing. Switch between sandbox and live mode there to see each set. A live secret key you create yourself is shown only once.

Step by step, per Stripe's docs (we didn't sign in to a Stripe account for this post):

  1. Go to the API keys page in the Dashboard (Stripe links it at dashboard.stripe.com/apikeys).
  2. Choose a sandbox to get test keys (pk_test_, rk_test_, sk_test_), or toggle to live mode for live keys.
  3. Copy the publishable key for your frontend. For the server, Stripe now points you to a restricted key.
  4. If you create a live secret or restricted key, copy it before you save it. Stripe's docs say you can't reveal it later, and Stripe can't recover a lost key. If that happens, rotate or delete it and make a new one.

Stripe's API keys docs page with the If you're new to Stripe box listing sandbox key prefixes pk_test_, rk_test_ and sk_test_, live prefixes pk_live_, rk_live_ and sk_live_, and a note that webhook secrets are separate The top of Stripe's API keys docs: the Dashboard page, sandbox and live prefixes, and where webhook secrets live. Screenshot, Oct 27, 2026.

That "shown once" rule is why people end up pasting a live key into a chat or a config file "just for now". Put it straight into your host's environment variables instead. Our guide to .env files (external) shows how, and the same idea applies if you are getting an OpenAI API key (external) for the same app.

What's the difference between Stripe test keys and live keys?

Stripe test keys work only in a sandbox: card networks don't process payments, and API calls return simulated objects. Live keys work in live mode, where real money moves. Test keys start with _test_ and live keys with _live_. Test keys are lower risk, but a test secret key still shouldn't ship in your frontend.

Sandbox (test)Live mode
Prefixespk_test_, rk_test_, sk_test_pk_live_, rk_live_, sk_live_
Real payments?No. Card networks and payment providers don't process them.Yes
Objects returnedSimulated (test customers, payments, refunds)Real account data
Use it forBuilding and testing your integrationAccepting real payments

Source: Stripe docs, API keys ("Sandbox versus live mode"), checked Oct 27, 2026.

Why care about stripe test keys at all, if they can't move money? A test secret key still opens your sandbox data and settings, and the code path that leaked it will leak the live key the day you switch over. Our scanner treats a test secret key as a warning for that reason (see the results below). For test card numbers, use Stripe's testing docs (external).

Should you use a Stripe restricted API key instead of a secret key?

Yes, in most cases. Stripe's docs call restricted API keys (rk_) the recommended type, especially for AI agents. You choose None, Read or Write per resource, which limits the damage if the key leaks. Stripe says to treat restricted keys with the same caution as secret keys: server only, never in your app.

What Stripe's docs said on Oct 27, 2026:

  • A secret key has unrestricted permissions, so Stripe doesn't recommend secret keys for new use cases and suggests migrating existing ones to restricted keys.
  • Your server sends a stripe restricted api key with API requests just like a secret key, but the key can only do what you gave it permission to do.
  • You can tag a restricted key as belonging to an autonomous agent. Agent-tagged keys get approval rules, so a named reviewer approves sensitive actions first.

Stripe's Restricted API keys docs page explaining that a restricted key assigns specific API permissions and is the recommended key type for AI agents Stripe's restricted API keys docs. Screenshot, Oct 27, 2026.

A worked example: a small app that only creates Checkout Sessions and reads their status needs write access to Checkout Sessions and read access to what it looks up, not refunds, payouts or customer exports. If that key leaks, the damage is limited to what you allowed. With an unrestricted sk_live_ key, it isn't. (Pick the exact permissions from Stripe's docs for your own integration.)

If an AI coding tool or agent is calling Stripe for you, this matters twice. Our Claude API key security guide (external) covers how to keep any agent key out of prompts and repos.

Is the Stripe webhook secret an API key?

No. The Stripe webhook secret (whsec_) is a per-endpoint signing secret, not an API key. Your server uses it to check the Stripe-Signature header and confirm an event really came from Stripe. It can't call Stripe's API, but anyone holding it could forge events your endpoint would trust, so keep it server-side.

From Stripe's webhook docs, checked Oct 27, 2026:

  • Signing secrets start with whsec_. For a Dashboard endpoint you find it in Workbench with Reveal secret. If you use the Stripe CLI, the secret is printed in your terminal when you run stripe listen.
  • Verify every event with the Stripe-Signature header and the endpoint secret.
  • Stripe recommends rolling signing secrets periodically, or when you suspect a compromise. You can expire the old secret at once or delay it for up to 24 hours.

Stripe's Manage webhook endpoints docs page, signature errors section, showing the Check the endpoint secret step Stripe's webhook docs, Check the endpoint secret (external). Screenshot, Oct 27, 2026.

Store whsec_ next to your other server secrets, not in frontend code. That matters below, because our scanner did not flag it.

We scanned a page with fake Stripe keys: here's what got flagged

We hid eight fake Stripe keys in a local demo page and scanned it with the Greenlight My App scanner. Live secret and restricted keys came back FAIL, their test versions WARN, and publishable keys produced no finding. The webhook secret and the organization key were not flagged, so check for those yourself.

How we ran it: one demo page with all eight fake strings in its JavaScript, then one page per key type, all served on our own machine and scanned with the scanner code that runs Greenlight My App's free scan (Oct 27, 2026, 6:52 to 6:53 AM ET). Each fake key was the prefix plus 24 X's.

Key type (fake)Scanner resultFinding title
sk_live_FAIL"Stripe live secret key is visible in your public code"
rk_live_FAIL"Stripe live secret key is visible in your public code"
sk_test_WARN"Stripe test secret key is visible in your public code (lower risk than a live key)"
rk_test_WARN"Stripe test secret key is visible in your public code (lower risk than a live key)"
pk_live_No finding (expected)Secrets check passed: "No secret keys found in your public code"
pk_test_No finding (expected)Secrets check passed
whsec_Not flagged in this testSecrets check passed
sk_org_Not flagged in this testSecrets check passed

Scan output for the demo page with eight fake Stripe keys: two FAIL rows reading Stripe live secret key is visible in your public code and two WARN rows reading Stripe test secret key is visible in your public code All eight fake keys on one page: 2 FAIL (live secret and restricted keys) and 2 WARN (test versions). Rendered from the scan output file; keys were fake.

Scan output for a demo page holding only a fake pk_live_ key: the exposed secrets check passes with No secret keys found in your public code A page with only a fake live publishable key: no finding, which matches Stripe's docs. Rendered from the scan output file.

What this means for you:

  • The scanner reports restricted keys under the secret-key title. If you see "Stripe live secret key is visible", search your code for both sk_live_ and rk_live_.
  • A publishable key in your app is fine. No finding is the right answer here.
  • Don't rely on the scan for whsec_ or sk_org_. In this test it didn't flag them. Search your built files for those prefixes by hand.
  • The scan reads your homepage and up to 15 of the scripts it loads. Keys hidden elsewhere, such as in source maps (external) or pages it didn't load, may not show up. "No finding" isn't a guarantee.

Taking payments in an app built with Lovable, Bolt, Cursor or v0? Run Greenlight My App's free scan (external) before launch. It looks for secret keys that shouldn't be there, including Stripe secret and restricted keys, in your homepage and up to 15 of the scripts it loads.

Leaked a Stripe secret key? Do this now

If a Stripe secret or restricted key leaks, rotate it in the Dashboard right away. Stripe's docs say to treat any exposure as a potential compromise, even if you aren't sure anyone saw it. Then move the new key into server environment variables, remove the old one from your code and history, and check the key's request logs.

  1. Rotate the key. On the API keys page, open the key's menu and choose Rotate key, and pick an expiration for the old key. Stripe's docs say the old and new keys can both work for up to 7 days. For a leak, choose Now if you can deploy the new key right away (your app's Stripe calls fail until you do), or the shortest expiration that gives you time to deploy.
  2. Put the new key in your server environment. Not in frontend code, and not in a file that gets committed. See what a .env file is (external).
  3. Find every copy of the old key. Check your built JavaScript, your source maps (external), your repo history and any shared chats or tickets.
  4. Check the key's request logs in the Dashboard for calls you don't recognize.
  5. Add an access policy. Stripe recommends access policies on all live keys, so a key only works from servers you know about.
  6. Switch to a restricted key if the leaked one was an unrestricted sk_live_ key.

Stripe's API keys docs, Rotate an API key section, showing that when you rotate a key in the Dashboard both the old and new keys work for up to 7 days Stripe's rotation steps, where you pick how long the old key keeps working (up to 7 days). Screenshot of docs.stripe.com/keys, Oct 27, 2026.

A worked example of the timing: you rotate a leaked sk_live_ key at 9:00 AM. If you picked a 7-day expiration, your server's old key keeps working, but an attacker could use the old key for up to 7 days too. For a real leak, keep that window as short as you can: deploy the new key, check the old key's request logs, then use Expire key on the old one.

Exposed a different kind of service key? The same steps apply, and our guide to an exposed Supabase service role key (external) walks through that case. For a full pre-launch pass, use the Lovable launch checklist (external).

FAQ

How do I find my Stripe API key? Open the API keys page in the Stripe Dashboard. Stripe's docs say you create, reveal, expire and rotate keys there for the account or sandbox you are viewing. Use a sandbox for test keys and toggle to live mode for live keys. A live secret key you create is shown once, so copy it before saving.

What is the Stripe API? The Stripe API is the set of web endpoints your app calls to work with payments and related objects, such as customers, payments, refunds and subscriptions. Your app proves which account is calling by sending an API key. Publishable keys handle browser-side steps; secret or restricted keys handle everything sensitive on your server.

Is the Stripe publishable key safe to share? Yes. Stripe's docs mark publishable keys (pk_test_, pk_live_) as safe to expose and say you can include them in frontend code. A publishable key can create tokens or payment methods but can't create charges or read account data. In our test, a page with only a publishable key produced no finding.

What's the difference between Stripe test and live keys? Test keys (_test_) work in a sandbox, where card networks don't process payments and the API returns simulated objects. Live keys (_live_) work in live mode, where real payments happen. Keep both secret if they are secret or restricted keys; only the publishable versions belong in your app.

What is a Stripe restricted key? A restricted key (rk_test_ or rk_live_) is a server-side key with only the permissions you choose: None, Read or Write per resource. Stripe recommends restricted keys over unrestricted secret keys, especially for AI agents. Stripe says to protect them like secret keys, so never put one in frontend code.

What should I do if my Stripe secret key is leaked? Rotate it in the Dashboard right away; Stripe says to treat any exposure as a potential compromise. Deploy the new key to your server environment, then expire the old one (choose Now when rotating, or use Expire key once you've deployed). Remove every copy from code, history and source maps, check the key's request logs, and add an access policy.

Check your app before you take payments

Taking payments in an app built with Lovable, Bolt, Cursor or v0? Run Greenlight My App's free scan before launch. It looks for secret keys that shouldn't be there, including Stripe secret and restricted keys, in your homepage and up to 15 of the scripts it loads. It's automated, read-only and educational, not a security audit, and "nothing found" isn't a guarantee that nothing is exposed.

Sources

On this site

Related posts

Greenlight My App is independent and not affiliated with or endorsed by Stripe, Lovable, StackBlitz (Bolt), Anysphere (Cursor) or Vercel (v0). Stripe is a trademark of its owner. All product names are trademarks of their owners. Stripe facts are paraphrased from Stripe's public docs as checked on Oct 27, 2026, and can change. This page is general information, not professional security advice, legal advice, a security audit or a guarantee. Check your own setup against the vendor's current docs.