# Secret leak API documentation — LeakWatch

> REST API to find leaked API keys and secrets in public repos: search by account, scan your git history, monitor continuously. OpenAPI schema, bearer auth.

Source: https://leakwatch.net/docs

---

[← LeakWatch](/)

# API documentation

Query the secrets LeakWatch detects in public repositories, from your own scripts, CI, or agents. REST over HTTPS, JSON responses, an OpenAPI schema for machine consumption.

[Interactive reference (Swagger) →](/api/v1/docs) · [OpenAPI schema](/api/v1/openapi.json)

## Quickstart

The read endpoints work without any credentials. Try one right now:

curl https://leakwatch.net/api/v1/leaks/stats

Every path below is relative to the base URL `https://leakwatch.net/api/v1`.

## Authentication

Account features — and the higher rate limits — need an API key. Create one in [your dashboard](/dashboard/api), then send it as a bearer token:

curl https://leakwatch.net/api/v1/leaks/search?account=octocat&source=github \\
  -H "Authorization: Bearer lw\_live\_your\_key\_here"

The key is shown **once**, when you create it: we store only a hash, so a lost key cannot be recovered — revoke it and create another. Treat it like a password; anyone holding it acts as your account. If you ever leak one, revoking it from the dashboard takes effect immediately.

## Public endpoints

These read from the public detection database and work with or without a key. Everything here is masked and detached from its repository.

Endpoint

Auth

Description

GET`/leaks/recent`

None

The 50 most recent detections. Secrets masked, repository never revealed.

GET`/leaks/search`

Optional

Aggregated counts for an account: total, distinct repositories, severity breakdown. Takes account and source.

GET`/leaks/stats`

None

Global statistics: all-time total, last 24 hours, severity breakdown.

GET`/detectors`

None

Catalogue of every secret type the engine recognises, with its severity.

GET`/health`

None

Liveness probe.

GET`/version`

None

API version.

### Example — recent detections

curl https://leakwatch.net/api/v1/leaks/recent

{
  "leaks": \[
    {
      "type": "AWS Access Token",
      "forge": "github",
      "masked\_secret": "AKIA...",
      "discovered\_at": "2026-08-12T09:14:22Z"
    }
  \]
}

### Example — leaks for an account

curl "https://leakwatch.net/api/v1/leaks/search?account=octocat&source=github"

{
  "account": "octocat",
  "source": "github",
  "total": 3,
  "distinct\_repos": 2,
  "by\_severity": { "critical": 2, "high": 0, "medium": 1, "low": 0 }
}

## Your account

These endpoints act on your own data and always require a key. What you get back follows your plan — `GET /me` tells you which one applies and how much quota is left, so integrations can check rather than guess.

Endpoint

Plan

Description

GET`/me`

Any

Your account: plan, linked identities, rate limit and remaining deep-scan quota.

GET`/me/leaks`

Any

Every secret found across your verified identities. Full values on paid plans only.

GET`/scans`

Any

Your recent deep scans.

POST`/scans`

Any (quota)

Scan the full git history of one of your repositories. Free plans get one per window.

GET`/scans/{id}`

Any

Progress of a scan — poll while queued or running.

GET`/scans/{id}/report`

Any

Everything currently known about the scanned repository, values in full.

GET`/monitors`

Any

Repositories you have under continuous monitoring.

POST`/monitors`

Solo / Team

Put one of your public repositories under continuous monitoring.

DELETE`/monitors/{id}`

Solo / Team

Stop watching a repository. Findings already detected are kept.

GET`/monitors/{id}/findings`

Solo / Team

Secrets found in one of your monitored repositories, values in full.

POST`/scan/content`

Any

Scan text you submit for secrets. Nothing is stored — ideal for a CI gate.

POST`/me/leaks/{id}/validate`

Solo / Team

Re-test one of your own leaked keys against its provider.

### Example — scan your own repository

\# Start the scan (returns immediately, status "queued")
curl -X POST https://leakwatch.net/api/v1/scans \\
  -H "Authorization: Bearer lw\_live\_your\_key\_here" \\
  -H "Content-Type: application/json" \\
  -d '{"repo\_full\_name": "octocat/hello-world"}'

# Poll until status is "success", then read the report
curl https://leakwatch.net/api/v1/scans/<id>/report \\
  -H "Authorization: Bearer lw\_live\_your\_key\_here"

### Full values and your plan

In `GET /me/leaks`, the `secret`, `commit_url` and `still_active` fields are populated on paid plans and `null` on the free plan — the fields are always present, so your parsing does not change. Scan reports and monitored-repository findings always return values in full: those are repositories you own, and a masked value would not tell you which key to revoke.

## 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
    }
  \]
}

**Nothing you send is stored.** The content is scanned in memory and discarded when the response is sent: no database row is written, and it is never forwarded to a third party — detection is local pattern matching, not a model call. Bodies are capped at 1 MiB; 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.

### 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.

## What the API never returns

These are deliberate limits, not gaps. LeakWatch publishes proof that a secret leaked — never a working copy of it:

-   **Secret values are masked.** Only the type prefix is shown (`AKIA…`, `ghp_…`). Full values are reserved for the verified owner of the account they belong to, so they can revoke them.
-   **The repository is never named** in the public feed — no repo name, no commit URL. Without it, the feed is not a map to live secrets.
-   **Liveness is not published.** Whether a key still works is the one detail with operational value to an attacker.
-   **Search is per account, aggregated.** Counts, not a list you can walk. Codeberg lookups are restricted to the account owner.

## Rate limits

Limits follow your plan and are counted per key, so two integrations behind one IP each get their own budget. Over the limit, the API answers `429` — back off and retry.

Plan

Limit

No key

per IP

Enough to try things out and for occasional use.

Free

60 req/min

Counted per key, not per IP.

Solo

300 req/min

Team

1,000 req/min

## Errors

Errors use standard HTTP status codes and carry a `detail` field explaining what went wrong.

Code

Meaning

401

Missing, malformed, or revoked API key.

403

Valid key, but not allowed — e.g. a Codeberg lookup for an account you have not verified.

422

Invalid parameters (unknown source, malformed account).

429

Rate limit exceeded.

## What's next

Webhooks — a URL called whenever a new secret is found in one of your repositories, instead of polling `/me/leaks`. Want that, or something else? [Tell me](/contact).

The [Swagger reference](/api/v1/docs) is generated from the running code — when in doubt, it is the source of truth.
