What to Do When an API Key Leaks: A Provider-Neutral Playbook
Last verified on Nov 8, 2026.
The order matters more than the provider. Revoke or rotate the key, work out where it was exposed, check the issuer's logs for use between exposure and revocation, then fix the path so it does not leak again. This is a generic playbook for GitHub tokens, Stripe keys, OpenAI keys, and any other bearer credential. For AWS specifics, including CloudTrail, see leaked AWS access key.
Step 1: Revoke or Rotate Before Anything Else
GitHub's guidance on removing sensitive data is blunt: if the sensitive data is a secret, the first step is to revoke or rotate it. After that, the secret can no longer be used for access, and rewriting history may not be warranted. Stripe says the same: rotate an exposed secret or restricted key immediately, even if you are not sure anyone saw it, and treat any exposure as potentially a compromise.
The mechanics differ by provider, and the differences matter.
- Stripe. Rotating a key in the Dashboard revokes it and creates a replacement, and by default both keys keep working for up to 7 days for a gradual migration. That grace period is for planned rotation. For a leaked key, expire the old one rather than leaving it live. An expired secret key can no longer make API calls.
- OpenAI. The advice is to create a replacement, update your applications, and revoke the old key once the replacement works. For a leaked key, make that sequence fast.
- GitHub. Delete or regenerate the token from the account or app that issued it, then confirm it no longer authenticates.
If a service depends on the key, accept a short outage over a long exposure. A replacement takes minutes, a leaked key takes your bill and your data.
Step 2: Find Where It Leaked
You are looking for every copy of the key, not only the one you were told about.
Git history. Deleting the file in a later commit leaves the key in history. These commands search all refs. -S finds commits that change the number of occurrences of a string, -G takes a regex:
git log --all --oneline -S'sk_live_' -- .
git log --all -p -G'sk_live_[A-Za-z0-9]+' --format='%h %an %ad'
git grep -n -I -E 'sk_live_[A-Za-z0-9]+' $(git rev-list --all)Check forks and clones as well. GitHub notes that after you rewrite and force push, commits may still be reachable in clones, forks, cached views on GitHub, and pull requests that reference them. You cannot clean other people's clones.
CI logs. Build output often prints environment variables or failed commands with credentials in arguments. With the GitHub CLI:
gh run list --limit 50 --json databaseId,createdAt,name
gh run view 1234567890 --log | grep -nE 'sk_live_|ghp_|github_pat_|AKIA'Also look at the logs of every other CI system and at job artifacts. See secrets in logs for the regexes that work across providers.
Client-side bundles. OpenAI warns against deploying keys in browsers or mobile apps, because malicious users can take the key and make requests on your behalf. Search the shipped JavaScript, not the source, because build tools inline environment variables:
curl -s https://example.com/ | grep -oE 'src="[^"]+\.js"' | cut -d'"' -f2 | while read p; do
curl -s "https://example.com$p" | grep -noE 'sk_live_[A-Za-z0-9]+|ghp_[A-Za-z0-9]{36}'
doneApps shipped to mobile devices need the same check on the unpacked package.
Other places: shared documents, chat messages, ticket attachments, container image layers, and Terraform state. For continuous checks on what is publicly reachable, see the exposure guide.
Step 3: Check the Issuer's Logs for Use
The question is whether anyone used the key between exposure and revocation. Only the issuer can tell you.
- Stripe logs API requests per key. Each key has a View request logs option, and Stripe advises checking a key's request logs before expiring it so you know who still uses it. After a leak, read them for unfamiliar addresses, operations your integration never calls, and refunds or payouts you did not make.
- GitHub records fine-grained personal access token activity in the organization audit log. The
personal_access_tokencategory covers it. Your own account's security log lists actions by actor, repository, and date. Filter on the repositories the token could reach and on clones and pushes you do not recognize. - OpenAI and other API providers. Look for a usage or activity page in the provider's dashboard that breaks usage down by key. A spike on a key you only use in a nightly job is the signal. If the provider offers no per-key view, ask their support for logs of the key.
Record timestamps: first exposure, the last legitimate use you can confirm, and revocation. Anything outside the legitimate pattern during that window is suspect. If the key reached customer data, involve whoever owns incident response and breach notification.
Step 4: Decide About History
After revocation, rewriting git history is optional. GitHub is explicit that it has side effects: re-contamination when someone pushes an old clone, lost work, changed commit hashes, broken diffs on closed pull requests, and lost signatures. It also points out that the rewrite can lead others directly to the removed data. Do it only if the repository contains data that stays sensitive after rotation, such as personal data, and use a tool like git-filter-repo with a plan for every collaborator's clone.
Step 5: Stop It Recurring
- Block secrets at push. GitHub's push protection blocks pushes containing supported secrets before they reach the repository, from the command line, the web UI, file uploads, and the REST API. For repositories it is off by default, and you or an organization owner must turn it on. A contributor can bypass it with a reason; each bypass creates an alert and an audit log entry, so review them.
- Scan what is already there. Secret scanning raises alerts in the repository's Security tab for secrets matching provider patterns, including already committed ones.
- Keep keys out of code. Use environment variables or a secrets vault, a unique key per team member or service, restricted scopes, and expiry dates. OpenAI recommends expiring keys and a regular rotation process.
- Know where each key is used. Stripe's guidance is that periodic rotation confirms you know where every key is used and that you can replace one at short notice. If rotating a key is scary, that is the thing to fix.
- Do not rely on the provider to catch it. Stripe may deactivate a key it detects as exposed and notify you, but does not guarantee detection of every exposed key, so do not wait for it.
Watching the Audit Trail in Sazabi
Sazabi, an AI-native observability and security platform, can stream a GitHub Enterprise Cloud audit log through GitHub's audit log streaming, including personal access token and deploy key activity, Actions secret changes, and Git events. That puts the issuer-side trail from Step 3 in the same place as your application logs. Audit log streaming is an enterprise feature. On Team, Free, and Pro plans the audit events stay in the GitHub UI.
Frequently Asked Questions
Should I revoke the key before or after I investigate?
Revoke first. A live key is a standing risk, and the provider logs that show how it was used are still there after revocation. The only reason to delay is a short overlap so a production service can switch to a replacement, and that overlap should be hours, not days.
Do I need to remove the key from git history?
Not to make it safe. Once the key is revoked it no longer grants access. GitHub says rewriting history may not be warranted after revoking, and warns it has side effects including breaking pull request diffs and risking re-contamination.
How do I know if a leaked API key was used?
Check the issuing provider: Stripe has per-key request logs, GitHub has security and audit logs, and most providers offer usage or activity pages. Look for requests from unfamiliar addresses, unusual volume, or operations your application never performs.
Is a key in a private repository still leaked?
Treat it as exposed. OpenAI notes that private repositories are more secure but that a data breach can still leak keys, and anyone with read access to the repo, its clones, and its CI logs can see it.
Sources
- For a leaked secret the first step is to revoke or rotate it; rewriting history may then be unnecessary, and commits can remain reachable in clones, forks, cached views, and pull requests (accessed Nov 8, 2026)
- Secret scanning generates alerts in the repository Security tab for supported secrets, and push protection bypasses create their own alerts (accessed Nov 8, 2026)
- Push protection blocks pushes that contain supported secrets before they reach the repository, including command line pushes, UI commits, file uploads, and REST API requests; repository push protection is disabled by default and logs bypasses to the audit log (accessed Nov 8, 2026)
- Stripe says to rotate an exposed secret or restricted key immediately even if unsure anyone saw it, and to audit API request logs for misuse; Stripe may deactivate keys it detects as exposed (accessed Nov 8, 2026)
- Rotating a Stripe key in the Dashboard leaves both keys working for up to 7 days; an expired secret key can no longer make API calls; request logs can be viewed per key (accessed Nov 8, 2026)
- OpenAI recommends against deploying keys in browsers or mobile apps, against committing keys, and recommends expiring keys and replacing then revoking the old one (accessed Nov 8, 2026)
- The GitHub organization audit log records activity related to fine-grained personal access tokens (accessed Nov 8, 2026)
- The GitHub security log can be searched by organization, actor, repository, action, country, and date (accessed Nov 8, 2026)
- git log -S finds commits that change the number of occurrences of a string, and -G finds commits whose diff matches a regular expression (accessed Nov 8, 2026)
- gh run view --log prints the full log of a workflow run or job (accessed Nov 8, 2026)
- Sazabi streams a GitHub Enterprise Cloud audit log, including personal access token and deploy key activity and Actions secret changes, through GitHub audit log streaming (accessed Nov 8, 2026)