Scan your own content
Send text — a diff, a file, an environment block — and get back the secrets found in it. The same engine and the same /detectors catalogue that power our public scanning. Useful as a pre-push hook or a CI gate that fails a build before a key ever reaches a public repository.
curl -X POST https://leakwatch.net/api/v1/scan/content \
-H "Authorization: Bearer lw_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"content": "AWS_SECRET=AKIAIOSFODNN7EXAMPLE\n", "filename": ".env"}'
{
"filename": ".env",
"count": 1,
"bytes_scanned": 41,
"secrets": [
{
"type": "AWS Access Token",
"severity": "critical",
"value": "AKIAIOSFODNN7EXAMPLE",
"line": 1
}
]
}Your content is never stored. It is scanned in memory and discarded when the response is sent, and it is never forwarded to a third party — detection is local pattern matching, not a model call. What we do keep is the result: how many secrets, of which types and severities, on how many bytes. That is what feeds the CI/CD tab of your dashboard and GET /scan/history, and it is all there is — no diff, no secret value, no line number. Bodies are capped at 1 MiB on Free and 5 MiB on paid plans; split larger inputs and scan them in parts.
Values are returned in full, since they came from the content you submitted. A hit is a pattern match, not proof the credential is live — this endpoint never tests it against its provider.
Quota. Free keys get 50 content scans per day (UTC), and the response carries quota_remaining so a job can warn before it hits the wall. Once it does, the answer is 402 — not 429, which stays reserved for going too fast. Paid plans have no daily quota and may send bodies up to 5 MiB.
A CI gate, without installing anything
No action to add, no binary to vendor, no runner image to rebuild: the job needs curl and jq, which every CI image already ships. Put your key in a secret named LEAKWATCH_API_KEY and fail the build on a non-zero count.
#!/bin/sh
# Fails the build if the diff about to be merged contains a secret.
set -eu
DIFF=$(git diff --unified=0 origin/main...HEAD)
COUNT=$(printf '%s' "$DIFF" | jq -Rs '{content: ., filename: "diff"}' | curl -sS -X POST https://leakwatch.net/api/v1/scan/content -H "Authorization: Bearer $LEAKWATCH_API_KEY" -H "Content-Type: application/json" --data-binary @- | jq '.count')
if [ "$COUNT" -ne 0 ]; then
echo "LeakWatch: $COUNT secret(s) in this diff — refusing to merge."
exit 1
fiDropped into GitHub Actions, that is a single step — and the same script runs unchanged in GitLab CI, CircleCI, or a local pre-push hook:
- name: Secret scan
env:
LEAKWATCH_API_KEY: ${{ secrets.LEAKWATCH_API_KEY }}
run: sh ci/leakwatch.shScan the diff rather than the whole tree: it is what changed that can leak, the payload stays small, and a key committed years ago will not fail every build from now on. For the whole tree — a first audit, or a nightly job — use the batch endpoint below.
Full recipes — GitHub Actions, GitLab CI, CircleCI, a pre-push hook, and what to do when the API answers something other than 200 — are in the CI/CD tab.
Many files in one call
POST /scan/batch takes a list of files and returns one result per file, in the order you sent them. It exists so that scanning a repository does not mean splitting the payload yourself — which is where "no install" quietly stops being true. Requires a paid plan; up to 200 files, 5 MiB total.
curl -X POST https://leakwatch.net/api/v1/scan/batch -H "Authorization: Bearer lw_live_your_key_here" -H "Content-Type: application/json" -d '{"files": [
{"filename": "config.py", "content": "DEBUG = True\n"},
{"filename": ".env", "content": "AWS_SECRET=AKIAIOSFODNN7EXAMPLE\n"}
]}'
{
"count": 1,
"bytes_scanned": 55,
"files": [
{ "filename": "config.py", "count": 0, "bytes_scanned": 14, "secrets": [] },
{
"filename": ".env",
"count": 1,
"bytes_scanned": 41,
"secrets": [
{
"type": "AWS Access Token",
"severity": "critical",
"value": "AKIAIOSFODNN7EXAMPLE",
"line": 1
}
]
}
]
}Over either cap the whole call is refused rather than truncated: a partial scan reported as a pass is worse than no scan at all.
Past runs
GET /scan/history returns your past runs, most recent first — counts only, kept for a bounded window that the response states in retention_days. The same list is the CI/CD tab of your dashboard.
curl https://leakwatch.net/api/v1/scan/history \
-H "Authorization: Bearer lw_live_your_key_here"
{
"retention_days": 90,
"runs": [
{
"id": "0d9f…",
"created_at": "2026-08-25T09:12:44Z",
"endpoint": "content",
"filename": "diff",
"file_count": 1,
"bytes_scanned": 8213,
"secret_count": 1,
"by_severity": { "critical": 1 },
"by_type": { "AWS Access Token": 1 }
}
]
}Checking whether your own key is still live
That is a separate endpoint, and it works from a leak id rather than a key value: POST /me/leaks/{id}/validate re-tests a credential that was found in one of your repositories, then updates still_active in GET /me/leaks. There is deliberately no endpoint that validates an arbitrary key you paste in: that would be a testing service for stolen credentials, which is not something we are willing to run.