> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.darknetsearch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Find an organization's exposed code

> Search GitHub, GitLab, Gists, Postman and Google for code and API collections that mention an organization, rank the findings by risk, export them, and list the developers behind its repositories.

Code exposure finds public code that mentions your organization. You give it keywords such as `acme.com` or an internal host name. It searches public code platforms for them and stores each match as a finding with a risk score, so you can read the riskiest ones first.

In the dashboard this is **Exposure → Code Exposure**. In the API reference the endpoints sit under the **Code Scan** tag, and the plan feature is **Exposed Code Detection**. Scans started in either place share one history and one allowance.

There are two kinds of scan:

* **Keyword scan.** Searches one source for your keywords. One request can start a keyword scan on up to five sources at once.
* **Contributor scan.** Lists the developers who contribute to the GitHub organizations and GitLab groups or projects you name.

Both run in the background: start the scan, poll it until it ends, then read or export what it found.

### Availability and cost

**Availability:** Exposed Code Detection. Every endpoint on this page needs it, including lists, results, exports and templates. [Plans and quotas](https://wiki.darknetsearch.com/api/guides/plans-and-quotas) lists what each plan includes.

**Cost:**

* **Keyword scans.** 1 unit of your monthly Code Exposure Keywords allowance for each unique keyword in a start request, however many sources search it. Keywords are compared without regard to case, so `acme.com` and `ACME.com` count once.
* **Contributor scans.** 1 unit of your monthly Code Exposure Contributors allowance for each scan.

The units are taken before any scan starts, all or nothing: if the allowance cannot cover every unique keyword, nothing starts and nothing is taken. A keyword start is refunded only when no source starts. Once one source starts, the call keeps every unit, including those for keywords whose source landed in `failures`. A contributor start is refunded on any non-`2xx` response. Scans that end `failed`, and scans you delete, keep their units. Polling, results, exports, lists and templates use no units.

Without the feature, or when your license is not active, every endpoint on this page returns `403`:

```json theme={"dark"}
{
  "detail": "This feature is not included in your plan."
}
```

When the month's allowance cannot cover the request, the start call returns `429` and nothing starts:

```json theme={"dark"}
{
  "detail": "You have reached your Code Exposure Keyword Search limit for this month.",
  "resource": "code_exposure_keyword_search",
  "period": "month",
  "limit": 10
}
```

A contributor start returns the same shape with `"resource": "code_exposure_contributor_search"` and `Code Exposure Contributor Search` in `detail`. `limit` is your monthly total, packs included. Retrying fails until the allowance resets on the 1st of the month or a pack is added.

A keyword start checks its body before the allowance, so an invalid body returns `400` and uses nothing. A contributor start checks the allowance first: once it is spent, every contributor start returns `429`, even one with an invalid body.

A `429` whose body reads `"message": "Rate limit exceeded"` is the per-second rate limit or the API Calls allowance, not a Code Exposure allowance. See [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md).

### Sources

Each keyword scan searches one source. Name it in `source`.

| `source` | What it searches | Keywords per scan | Per-keyword setting |
| - | - | - | - |
| `github` | File contents in public GitHub repositories | 1 or more | `search_type` |
| `gitlab` | File contents in the gitlab.com projects and users you list in `options.projects` | Exactly 1 | — |
| `gist` | Public GitHub Gists | 1 or more | `operator` |
| `google` | Google results on code hosts, raw-file hosts, paste sites, snippet sites, CI logs, docs and wikis | 1 or more | `search_type` |
| `postman` | Public Postman API collections | 1 or more | — |

#### How keywords match

A finding is kept only when your keyword appears in its text: the file content, snippet, title, file name, description or URL. Matching ignores case. Gist is the exception: it keeps what the Gist search returns for the operators below. On `github` and `google`, `search_type` sets how strict the match is:

| `search_type` | A finding is kept when |
| - | - |
| `exact_phrase` | The whole keyword appears as one phrase. This is the default. |
| `all_words` | Every word of the keyword appears, in any order. |
| `any_word` | At least one word of the keyword appears. |

`gitlab` always matches the exact phrase. On `gist`, `operator` turns the keyword into a Gist search filter:

| `operator` | The keyword is |
| - | - |
| `default` | A phrase to search for. This is the default. |
| `user` | A GitHub user name. The scan returns that user's gists. |
| `extension` | A file extension, such as `env`. |
| `filename` | A file name, such as `.env`. |

`search_type` and `operator` are not checked when you start a scan. Send one of the values above.

### Start keyword scans

[`POST /api/code_scan/keyword_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/keyword_scans/) takes a `sources` list. Each entry starts one keyword scan.

```bash theme={"dark"}
curl https://client-api.leak.center/api/code_scan/keyword_scans/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sources": [
      {
        "source": "github",
        "keywords": [
          {"keyword": "acme.com", "search_type": "exact_phrase"},
          {"keyword": "acme-internal"}
        ],
        "options": {"max_results": 200}
      },
      {
        "source": "gitlab",
        "keywords": [{"keyword": "acme.com"}],
        "options": {"projects": ["acme-group/api", "acme-group/web"]}
      },
      {
        "source": "postman",
        "keywords": [{"keyword": "ACME.com"}]
      }
    ]
  }'
```

This request uses 2 units: `acme.com` and `acme-internal`. The same keyword on GitLab and Postman counts once.

The body rules:

* **`sources`**: 1 to 5 entries, each `source` at most once.
* **`keywords`**: at least one per source, and exactly one for `gitlab`. A keyword may appear only once per source. Blank keywords are refused, and spaces around a keyword are trimmed.
* **`options`**: optional, an object of settings for that source. See [Source options](#source-options). Keywords never go inside `options`.

The call returns `201` with the scans that started and the sources that did not:

```json theme={"dark"}
{
  "scans": [
    {
      "id": 4127,
      "source": "github",
      "status": "queued",
      "parameters": {
        "keywords": [
          {"keyword": "acme.com", "search_type": "exact_phrase"},
          {"keyword": "acme-internal"}
        ],
        "max_results": 200
      },
      "result_count": 0,
      "error": null,
      "created_at": "2026-10-07T09:14:02.481203",
      "started_at": null,
      "finished_at": null
    },
    {
      "id": 4128,
      "source": "gitlab",
      "status": "queued",
      "parameters": {
        "keyword": "acme.com",
        "projects": ["acme-group/api", "acme-group/web"]
      },
      "result_count": 0,
      "error": null,
      "created_at": "2026-10-07T09:14:02.913877",
      "started_at": null,
      "finished_at": null
    }
  ],
  "failures": [
    {
      "source": "postman",
      "status_code": 429,
      "detail": {
        "detail": "Too many active jobs for this organization (10/10). Please wait for current jobs to complete."
      }
    }
  ]
}
```

Keep each scan's `id`. `parameters` shows the options and keywords the scan runs with. Times are UTC.

Always read `failures`. A source in it did not start, and its keywords are still counted. Start it again after the cause clears, which uses units again. If no source starts at all, the call returns the first failure's status and body instead of `201`, and every unit is refunded.

### Source options

Options are not checked when you start a scan. A misspelled name is ignored and the scan runs with that option's default.

#### GitHub

| Option | What it does |
| - | - |
| `repos` | Search only these repositories, written `owner/repo`. |
| `users` | Search only repositories owned by these GitHub accounts. |
| `include_languages` | Search only files in these languages, such as `["Python"]`. |
| `include_paths` | Keep only findings whose file path contains one of these strings, such as `config/`. |
| `exclude_paths` | Drop findings whose file path contains one of these strings, such as `test/`. |
| `since`, `until` | Keep only files changed in this window, as ISO 8601 timestamps such as `2026-01-01T00:00:00Z`. Each file's commit history is read, so the scan takes longer. |
| `regex_patterns` | Extra regular expressions to flag in each snippet. A match is added to `patterns_matched` and raises `risk_score`. |
| `fetch_content` | Default `true`. With `false` the scan does not read the files, so findings have no snippet, no pattern matches and a `risk_score` of 0. |
| `max_results` | Findings per keyword. Default and maximum 1,000. |

#### GitLab

| Option | What it does |
| - | - |
| `projects` | **Required.** gitlab.com projects or users to search. A project is `acme-group/api` or `https://gitlab.com/acme-group/api`. A single name such as `acme-dev` is a user, and the scan searches that user's first 10 public projects. |
| `max_results` | Findings for the whole scan, shared across the listed projects. Default 100. |
| `regex_patterns` | Extra regular expressions to flag in each snippet. |
| `fetch_content` | Default `true`. With `false` the scan does not read the files. A finding is then kept only when the keyword appears in its file path or URL, so most findings are dropped. Leave it `true`. |

GitLab reads each file from the project's `main` branch, and every finding's `url` points to that branch. A project whose default branch has another name returns few or no findings.

> [!WARNING]
>
> **A GitLab scan without `options.projects` still starts and uses its unit.** It then ends `failed` with `"error": "projects or users parameter is required"`. Always send at least one project or user.

#### Gist

| Option | What it does |
| - | - |
| `pages` | Result pages to read per keyword, 1 to 10. Default 3. |
| `max_results` | Findings per keyword. Default 50. |
| `include_anonymous` | Default `false`. With `true` the search includes anonymous gists. |
| `fork_only` | Default `false`. With `true` the search returns forked gists only. |
| `regex_patterns` | Extra regular expressions to flag in each snippet. |

#### Google

| Option | What it does |
| - | - |
| `categories` | Which groups of sites to search. Default: all six. `public_forge` (code hosts and API tools such as GitLab, Bitbucket, Codeberg and Postman), `raw_endpoint` (raw-file hosts such as raw\.githubusercontent.com), `paste` (paste sites such as Pastebin), `snippets` (snippet sites such as gist.github.com, JSFiddle and CodePen), `ci_logs` (CI logs on GitHub Actions, GitLab, CircleCI and Netlify), `docs_wikis` (docs and wikis such as Read the Docs, Google Docs and Notion). |
| `custom_sites` | Up to 2 extra sites to search, such as `docs.acme.com`. Entries after the second are ignored. |
| `exclude_sites` | Sites to skip, written exactly as the site name, such as `pastebin.com`. |
| `date_range` | Only results from the past day (`d`), week (`w`), month (`m`) or year (`y`). Leave it out for any date. |
| `cap` | Findings per keyword. Default 300. The cap is checked after each site, and one site can add up to 100 findings, so a keyword can pass the cap by up to 99. |

#### Postman

| Option | What it does |
| - | - |
| `pages` | Result pages to read per keyword, 1 to 10. Default 3. |
| `max_results` | Findings per keyword, 1 to 500. Default 50. |

### Follow a scan

Fetch each scan with [`GET /api/code_scan/keyword_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/) until `status` is `finished` or `failed`. Poll every 30 seconds or slower.

```bash theme={"dark"}
curl https://client-api.leak.center/api/code_scan/keyword_scans/4128/ \
  -H "X-API-Key: YOUR_API_KEY"
```

```json theme={"dark"}
{
  "id": 4128,
  "source": "gitlab",
  "status": "finished",
  "parameters": {
    "keyword": "acme.com",
    "projects": ["acme-group/api", "acme-group/web"]
  },
  "result_count": 0,
  "error": null,
  "created_at": "2026-10-07T09:14:02.913877",
  "started_at": "2026-10-07T09:14:05.120044",
  "finished_at": "2026-10-07T09:16:41.877310"
}
```

`status` takes four values:

* **`queued`**: waiting to run.
* **`running`**: searching. Findings are saved while it runs, so you can read them already.
* **`finished`**: done. It may have found nothing.
* **`failed`**: done, with an error in `error`. Findings saved before the error are kept. The platform does not retry it. Start a new scan to try again, which uses units again.

`result_count` is the number of findings. The [scan list](#list-and-delete-scans) fills it in. For a single scan, read `total` from its results.

### Read the findings

[`GET /api/code_scan/keyword_scans/{scan_id}/results/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/results/) returns one scan's findings, riskiest first.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/code_scan/keyword_scans/4127/results/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "per_page=100"
```

* `page` starts at 1. `per_page` defaults to 20, max 100. A page past the end returns an empty `items` list.
* `sort_by` takes `risk_score` (the default), `created_at` or `id`. `sort_order` takes `desc` (the default) or `asc`. Ties are broken by `id`.
* `created_from` and `created_to` (ISO 8601) keep findings saved in that window.

```json theme={"dark"}
{
  "total": 1,
  "page": 1,
  "per_page": 100,
  "total_pages": 1,
  "items": [
    {
      "id": 88213,
      "source": "github",
      "data": {
        "keyword": "acme.com",
        "repo": "acme-dev/deploy-scripts",
        "path": "config/settings.py",
        "url": "https://github.com/acme-dev/deploy-scripts/blob/main/config/settings.py",
        "title": "settings.py",
        "language": "Python",
        "file_size": 0,
        "commit_date": "any",
        "snippet": "DB_HOST = \"db.acme.com\"\nDB_PASSWORD = \"example-not-a-real-secret\"",
        "patterns_matched": [
          {
            "rule": "generic-api-key",
            "description": "Detected a Generic API Key, potentially exposing access to various services and sensitive operations.",
            "match": "detected"
          }
        ],
        "ai_risk_score": 7,
        "ai_rationale": "A settings file for deployment scripts. It names a database host on acme.com and sets a database password in plain text.",
        "risk_score": 7,
        "found_at": "2026-10-07T09:20:41.118204"
      },
      "result_hash": "9f2c6d0e4b1a7c3e5d8f0a2b4c6e8f1a3b5d7e9f0a2c4e6b8d0f1a3c5e7b9d2f",
      "status": "new",
      "first_seen_at": "2026-10-07T09:20:41.130512",
      "status_updated_at": null,
      "created_at": "2026-10-07T09:20:41.131027"
    }
  ]
}
```

#### What each finding gives you

* **`data`**: the finding itself. The fields depend on the source. Every source gives `keyword`, `url` and `snippet`.
  * `github`: `repo`, `path`, `title`, `language`, `file_size` and `commit_date` (`any` when you set no `since` or `until`).
  * `gitlab`: `project` and `path`.
  * `gist`: `gist_id`, `username`, `filename` and `description`.
  * `google`: `title`, `site` (the site searched), `platform` and `content_type`.
  * `postman`: `collection_name`, `username` and `description`.
* **`data.risk_score`**: how risky the finding looks, from 0 to 10. Higher is riskier. It is the higher of two scores: the pattern score and the AI assessment. On GitHub, GitLab and Gist findings, each match of a known secret format, such as a private key, cloud access key or API token, adds 2 to 4 points to the pattern score. A Postman finding scores 1 when its collection text mentions an API, a key or a token. The dashboard marks 7 and above in red and 4 to 6 in yellow. A finding without a score sorts as 0.
* **`data.ai_risk_score`** and **`data.ai_rationale`**: the AI assessment of the snippet, as a score from 0 to 10 and a short text that explains it. Every finding with a snippet is assessed, on every source, Google and Postman included. GitHub, GitLab, Gist and Postman findings always carry both fields, with `0` and an empty text when no assessment ran. Google findings carry them only when one ran. The dashboard shows `ai_rationale` as the risk explanation.
* **`data.patterns_matched`**: the secret patterns found in the snippet, each with a `rule` and a `description`. On Postman, the `api-related` rule is not a secret format. It marks a collection whose text mentions an API, a key or a token.
* **`result_hash`**: identifies the file, gist, collection or page. Two findings with the same hash, in any of your scans, are the same item. GitLab is the exception: its hash covers only the file path, so the same path in two projects, such as `README.md`, shares a `result_hash` and a `first_seen_at`. Compare `data.project` as well before you treat two GitLab findings as one.
* **`first_seen_at`**: when your organization's scans first found this item. A date older than the scan means an earlier scan already found it.
* **`status`**: `new` on every finding.

### Export the findings

[`GET /api/code_scan/keyword_scans/{scan_id}/export/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/export/) downloads every finding of one scan as a file. Choose the format with `file_format`: `csv` (the default), `json` or `xml`.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/code_scan/keyword_scans/4127/export/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "file_format=json" \
  -o acme-github-findings.json
```

> [!WARNING]
>
> **Use `file_format`, never `format`.** The API reserves `format` for itself: `?format=csv` returns `404` and `?format=json` is ignored, so you get CSV.

* The file is named `job_<scan id>_results.<format>` in the `Content-Disposition` header.
* Findings are in the order they were saved, not by risk.
* Each record has `id`, `module` and `created_at`, then the `data` fields flattened into dotted keys such as `patterns_matched[0].rule`. `module` holds the scanner's name for the source, such as `github_search`.
* Every `data` value is written as a string, in all three formats.
* In XML, `id`, `module` and `created_at` are attributes of each `result` element. In keys, dots and opening brackets become underscores and closing brackets are dropped, so `patterns_matched[0].rule` becomes `patterns_matched_0_rule`.
* A scan with no findings returns `404` with `{"detail": "No results found for this job"}`.

Leave the `Accept` header out, or send `*/*` as curl does. An `Accept: text/csv` header returns `406`.

### List and delete scans

[`GET /api/code_scan/keyword_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/) lists your organization's keyword scans, newest first, including the ones started in the dashboard.

```bash theme={"dark"}
curl -G https://client-api.leak.center/api/code_scan/keyword_scans/ \
  -H "X-API-Key: YOUR_API_KEY" \
  --data-urlencode "status=running" \
  --data-urlencode "source=github"
```

* `page` starts at 1. `per_page` defaults to 10, max 100.
* `status` filters on one status, such as `running`. `source` filters on one source.
* `created_from` and `created_to` (ISO 8601) keep scans created in that window.
* The page has the same envelope as the results: `total`, `page`, `per_page`, `total_pages` and `items`.

[`DELETE /api/code_scan/keyword_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/DELETE/api/code_scan/keyword_scans/\{scan_id}/) removes a scan and all of its findings. It returns `204` with no body and refunds no units. Deleting a running scan stops it.

```bash theme={"dark"}
curl -X DELETE https://client-api.leak.center/api/code_scan/keyword_scans/4127/ \
  -H "X-API-Key: YOUR_API_KEY"
```

### Find contributors

A contributor scan lists the developers behind an organization's public repositories. Use their user names as `users` in a GitHub keyword scan, or as `projects` entries in a GitLab one, to search code they publish under their own accounts.

**1. Start the scan**

[`POST /api/code_scan/contributor_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/contributor_scans/) takes `custom_links`: a list of full URLs.

```bash theme={"dark"}
curl https://client-api.leak.center/api/code_scan/contributor_scans/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"custom_links": ["https://github.com/acme", "https://gitlab.com/acme-group"]}'
```

* **GitHub**: a GitHub organization URL, such as `https://github.com/acme`. The scan reads the contributors of its first 100 public repositories, skips forks and leaves out bots.
* **GitLab**: a gitlab.com group or project URL. A group scan reads up to 20 of its projects.
* Each entry must be a full URL, scheme included. Links to other hosts are ignored.

Send `custom_links`. Without it, the scan falls back to GitHub and GitLab links stored for your organization. The API cannot set those, and with none stored the call returns `400` and uses no unit.

The call returns `201`:

```json theme={"dark"}
{
  "id": "6f1d2c3b-4a5e-4f60-8b7c-9d0e1f2a3b4c",
  "organization_links": ["https://github.com/acme", "https://gitlab.com/acme-group"],
  "status": "PENDING",
  "message": "Contributor scanning job created",
  "progress": 0,
  "result": null,
  "error": null,
  "created_at": "2026-10-07T09:30:12.004511"
}
```

The `id` is a string, not a number. URL-encode it when you put it in a path.

**2. Poll until it ends**

Fetch the scan with [`GET /api/code_scan/contributor_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/contributor_scans/\{scan_id}/) until `status` is `SUCCESS` or `FAILURE`. Contributor statuses are uppercase:

* **`PENDING`**: waiting to run.
* **`RUNNING`**: scanning. `progress` moves from 0 to 100 and `message` says what the scan is doing.
* **`SUCCESS`**: done. Read `result`.
* **`FAILURE`**: done, with the reason in `error`.

```bash theme={"dark"}
curl https://client-api.leak.center/api/code_scan/contributor_scans/6f1d2c3b-4a5e-4f60-8b7c-9d0e1f2a3b4c/ \
  -H "X-API-Key: YOUR_API_KEY"
```

**3. Read the contributors**

On `SUCCESS`, `result` lists the developers found:

```json theme={"dark"}
{
  "id": "6f1d2c3b-4a5e-4f60-8b7c-9d0e1f2a3b4c",
  "organization_links": ["https://github.com/acme", "https://gitlab.com/acme-group"],
  "status": "SUCCESS",
  "message": "Contributor scanning completed successfully",
  "progress": 100,
  "result": {
    "contributors_discovered": 2,
    "success": true,
    "organization_links_scanned": ["https://github.com/acme", "https://gitlab.com/acme-group"],
    "unique_contributors": [
      {
        "username": "acme-dev",
        "display_name": "acme-dev",
        "profile_url": "https://github.com/acme-dev",
        "platform": "github",
        "repo_count": 14
      },
      {
        "username": "acme-build",
        "display_name": "acme-build",
        "profile_url": null,
        "platform": "gitlab",
        "repo_count": 3
      }
    ]
  },
  "error": null,
  "created_at": "2026-10-07T09:30:12.004511"
}
```

* **`unique_contributors`**: one entry per developer, merged across all links. On GitHub, `repo_count` counts the public repositories the developer owns or is a member of, up to 50. On GitLab, it counts the matched account's other projects, `profile_url` is `null`, and `username` is the commit author's name when no GitLab account matches. GitLab matches an account by searching the commit email, then the author name, and takes the first hit.
* **`contributors_discovered`**: how many developers the scan found.
* **`success`**: `false` when the scan found nobody. The status is still `SUCCESS`.

`result` also carries `link_results`, with the detail for each link.

[`GET /api/code_scan/contributor_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/contributor_scans/) lists your contributor scans, newest first. `per_page` defaults to 50, max 100. It filters on `status` (uppercase) and on `created_from` and `created_to`. The `status` filter uses the last status the platform recorded: a scan moves from `PENDING` to `RUNNING` only when it is fetched while it runs, so `status=RUNNING` can miss a running scan. Fetch a scan by its `id` for its live status. Contributor scans cannot be deleted.

### Save settings as a template

A template stores a named set of scan settings for your organization. You can create templates and list them.

[`POST /api/code_scan/templates/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/templates/) takes three fields:

* **`template_name`**: required. Names are unique within your organization.
* **`template_type`**: `scan` (the default) or `report`.
* **`settings`**: required. Any JSON object, stored as you send it.

```bash theme={"dark"}
curl https://client-api.leak.center/api/code_scan/templates/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "Acme weekly",
    "settings": {
      "sources": [
        {"source": "github", "keywords": [{"keyword": "acme.com"}], "options": {"max_results": 200}}
      ]
    }
  }'
```

The call returns `201` with the template's `id`, `template_name`, `template_type`, `settings`, `created_at` and `updated_at`. [`GET /api/code_scan/templates/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/templates/) lists templates newest first. It filters on `template_type`, `created_from` and `created_to`, and `per_page` defaults to 20.

A start request cannot name a template. To run a template, read its `settings` and send them in a [start request](#start-keyword-scans). Templates saved in the dashboard appear in this list too, and their `settings` use the dashboard's own format, not the start request body. Templates cannot be changed or deleted through the API.

### Limits

* **10 active keyword scans.** Your organization can have at most 10 keyword scans `queued` or `running` at once, dashboard scans included. Each source in a start request is one scan. A source over the limit lands in `failures` with `status_code` `429`, and its keywords are still counted. To avoid that, list your `queued` and `running` scans first and keep their `total` plus the sources you send at 10 or fewer.
* **Starts per minute.** Your organization can start up to 10 keyword scans and 2 contributor scans per minute. The two limits are counted separately. Each source in a start request is one keyword scan, so two requests with five sources each use the whole minute. A keyword source over that limit lands in `failures` with `429` and an `error` field that starts with `Rate limit exceeded`. A contributor start over it returns that `429` and uses no unit. Start again a minute later.
* **A busy scanner.** When the scanning queue is full, a source fails with `502`. Start it again later.
* **Findings per keyword.** GitHub keeps up to 1,000, Google 300, Gist 50 and Postman 50 by default. GitLab keeps up to 100 for the whole scan. Google can pass its cap by up to 99, as `cap` explains. Change them with the [source options](#source-options).
* **Trailing slash.** Every path ends with `/`. A path without it returns a `301` redirect, and a `POST` or `DELETE` sent to it is not carried out.
* **No schedules.** The API has no scheduled scans. To scan on a schedule, start scans from your own scheduler.

### Errors

| Status | When | Body |
| - | - | - |
| `400` | The keyword start body breaks a [body rule](#start-keyword-scans) | `{"sources": ...}` with a message such as `Each source may appear only once.`, `GitLab takes only one keyword.`, `Each keyword may appear only once per source.` or `Send keywords in the keywords list, not inside options.` |
| `400` | A `custom_links` entry is not a full URL | `{"custom_links": {"0": ["Enter a valid URL."]}}` |
| `400` | A contributor start has no links to scan | `{"detail": "No links to scan. Either provide custom_links or configure organization GitHub/GitLab URLs"}` |
| `400` | A template with that name exists, or the template body is invalid | `{"detail": "Template name already exists"}`, or the field and its message |
| `403` | Your plan lacks Exposed Code Detection, or your license is not active | See [Availability and cost](#availability-and-cost) |
| `404` | No keyword scan or contributor scan with that id | `{"detail": "Job not found"}` or `{"detail": "Contributor scan job not found"}` |
| `404` | Export of a scan with no findings | `{"detail": "No results found for this job"}` |
| `404` | Export with `?format=csv` | `{"detail": "Not found."}` |
| `406` | Export with an `Accept` header the API cannot serve | `{"detail": "Could not satisfy the request Accept header."}` |
| `422` | `per_page` above 100, `page` below 1, or an unknown `sort_by`, `sort_order` or `file_format` | `{"detail": [...]}` naming the parameter. For an unknown `file_format`, it names `format`. |
| `429` | The Code Exposure allowance cannot cover the start | See [Availability and cost](#availability-and-cost) |
| `429` | No keyword source started because of the 10-scan limit or the per-minute start limit, or a contributor start hit the per-minute start limit | The first failure's body, or `{"error": "Rate limit exceeded: ..."}`. See [Limits](#limits) |
| `429` | The per-second rate limit or the API Calls allowance | `{"message": "Rate limit exceeded", ...}`. See [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md) |
| `502` | The scanning service is unavailable, or no source started because the queue is full | `{"detail": "The requested service is temporarily unavailable."}` |
| `504` | The scanning service timed out | `{"detail": "The requested service timed out."}` |

[Errors](https://wiki.darknetsearch.com/api/guides/errors.md) explains every body the API returns.

### Endpoints

| Action | Endpoint |
| - | - |
| Start keyword scans | [`POST /api/code_scan/keyword_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/keyword_scans/) |
| List keyword scans | [`GET /api/code_scan/keyword_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/) |
| Get a keyword scan and its status | [`GET /api/code_scan/keyword_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/) |
| Delete a keyword scan and its findings | [`DELETE /api/code_scan/keyword_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/DELETE/api/code_scan/keyword_scans/\{scan_id}/) |
| List a keyword scan's findings | [`GET /api/code_scan/keyword_scans/{scan_id}/results/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/results/) |
| Export a keyword scan's findings | [`GET /api/code_scan/keyword_scans/{scan_id}/export/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/keyword_scans/\{scan_id}/export/) |
| Start a contributor scan | [`POST /api/code_scan/contributor_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/contributor_scans/) |
| List contributor scans | [`GET /api/code_scan/contributor_scans/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/contributor_scans/) |
| Get a contributor scan and its contributors | [`GET /api/code_scan/contributor_scans/{scan_id}/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/contributor_scans/\{scan_id}/) |
| Create a scan template | [`POST /api/code_scan/templates/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/POST/api/code_scan/templates/) |
| List scan templates | [`GET /api/code_scan/templates/`](https://client-api.leak.center/scalar-docs/#tag/code-scan/GET/api/code_scan/templates/) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.