keyprobe is a small Python CLI for detecting and validating API keys across multiple providers.
It supports:
- single-key mode
- interactive prompt mode
- batch validation from text, CSV, JSON, or stdin
- optional export of results to a log file
- an installable
keyprobeterminal command
| Provider | Key format example | Validation approach |
|---|---|---|
| OpenAI | sk-... |
GET /v1/models |
| Anthropic | sk-ant-... |
GET /v1/models |
AIza... |
checks Gemini/AI Studio and YouTube Data API | |
| Groq | gsk_... |
GET /openai/v1/models |
| GitHub | ghp_..., github_pat_... |
GET /user |
| Stripe | sk_live_..., sk_test_... |
GET /v1/balance |
| AWS | AKIA..., ASIA... |
format check plus CLI guidance |
| HuggingFace | hf_... |
GET /api/whoami |
| Cohere | 40-char alphanumeric | POST /v1/check-api-key |
| Mistral | 32-char alphanumeric | GET /v1/models |
| Replicate | r8_... |
GET /v1/account |
| Pinecone | pckey_... |
GET /indexes |
| Cloudflare | cfk_..., cfut_..., cfat_... |
GET /user/tokens/verify |
| X (Twitter) | app-only bearer token | GET /2/users/by/username/xdevelopers |
Create and activate a virtual environment, then install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf you want a real terminal command named keyprobe, install the project in editable mode:
pip install -e .Run the CLI with either:
keyprobe
python3 keyprobe.pyIf your shell exposes python instead of python3, that works too.
When launched without arguments, the CLI shows a startup panel with the most useful commands.
keyprobe sk-ant-myanthropickey...
# or:
python3 keyprobe.py sk-ant-myanthropickey...keyprobe
# or:
python3 keyprobe.pyYou can load candidates from:
- plain text files with one value per line
- CSV files
- JSON files
- stdin with
--batch -
Create a text file with one value per line:
# comments are allowed
sk-ant-example...
ghp_example...
Then run:
keyprobe --batch keys.txtValidate a JSON or CSV export:
keyprobe --batch findings.json
keyprobe --batch findings.csvPaste a large blob directly through stdin:
pbpaste | keyprobe --batch -
keyprobe --batch - < findings.jsonAuto-name the export file:
keyprobe --batch keys.txt --exportChoose the export path explicitly:
keyprobe --batch keys.txt --export results.log
keyprobe sk-mykey --export mykey_result.logkeyprobe --helpRun the basic test suite with:
python3 -m unittest discover -s tests -vEach key produces:
- provider name
- masked key value
- status such as
ACTIVE,INVALID,RATE-LIMITED, orUNKNOWN - human-readable detail
- a matching
curlexample for manual verification
At the end of a run, keyprobe prints an active-key summary. If active keys are found, this final summary shows their full uncensored values, so avoid running it while screen sharing or saving terminal output somewhere public.
Before detection, keyprobe also strips common wrappers around pasted secrets, such as Authorization: Bearer ... or cookie:name=..., while preserving real key prefixes like sk_, pk_, ghp_, and similar provider-specific formats.
In bulk JSON and CSV inputs, keyprobe prefers fields such as match, key, token, secret, value, and similar names when they exist, then deduplicates candidates and skips obvious non-key values.
For AWS access key IDs, keyprobe can only confirm the format. Full validation requires the paired secret key.
Google API keys are checked against both Gemini/AI Studio and the YouTube Data API, since the same AIza... key format can be used across multiple Google services.
X/Twitter bearer tokens use a conservative heuristic matcher because X's official docs describe the token format as unspecified.
keyprobe/
├── keyprobe.py
├── pyproject.toml
├── requirements.txt
├── README.md
├── tests/
│ ├── test_keyprobe.py
│ └── test_providers.py
└── providers/
├── __init__.py
├── anthropic.py
├── aws.py
├── cloudflare.py
├── cohere.py
├── github.py
├── google.py
├── groq.py
├── huggingface.py
├── mistral.py
├── openai.py
├── pinecone.py
├── replicate.py
├── stripe.py
└── twitter.py
keyprobe.pycontains the CLI flow, rendering, and export logic.providers/__init__.pyauto-discovers provider modules and selects the first match.- Each provider module exposes
detect()andvalidate().
Add a new file in providers/, for example providers/myprovider.py:
import re
import httpx
NAME = "MyProvider"
PATTERN = re.compile(r"^myprefix_[A-Za-z0-9]{32}$")
CURL = lambda key: f'curl https://api.myprovider.com/me -H "Authorization: Bearer {key}"'
ENDPOINT = "https://api.myprovider.com/me"
def detect(key: str) -> bool:
return bool(PATTERN.match(key))
def validate(key: str) -> dict:
r = httpx.get(ENDPOINT, headers={"Authorization": f"Bearer {key}"}, timeout=10)
if r.status_code == 200:
return {"status": "active", "detail": "Key valid"}
return {"status": "invalid", "detail": f"HTTP {r.status_code}"}On the next run, providers/__init__.py will auto-discover it if the module defines both detect() and validate().