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.
Availability and cost
Availability: Exposed Code Detection. Every endpoint on this page needs it, including lists, results, exports and templates. 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.comandACME.comcount once. - Contributor scans. 1 unit of your monthly Code Exposure Contributors allowance for each scan.
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:
429 and nothing starts:
"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.
Sources
Each keyword scan searches one source. Name it insource.
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. Ongithub and google, search_type sets how strict the match is:
gitlab always matches the exact phrase. On gist, operator turns the keyword into a Gist search filter:
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/ takes a sources list. Each entry starts one keyword scan.
acme.com and acme-internal. The same keyword on GitLab and Postman counts once.
The body rules:
sources: 1 to 5 entries, eachsourceat most once.keywords: at least one per source, and exactly one forgitlab. 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. Keywords never go insideoptions.
201 with the scans that started and the sources that did not:
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
GitLab
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.
Gist
Postman
Follow a scan
Fetch each scan withGET /api/code_scan/keyword_scans/{scan_id}/ until status is finished or failed. Poll every 30 seconds or slower.
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 inerror. 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 fills it in. For a single scan, read total from its results.
Read the findings
GET /api/code_scan/keyword_scans/{scan_id}/results/ returns one scan’s findings, riskiest first.
pagestarts at 1.per_pagedefaults to 20, max 100. A page past the end returns an emptyitemslist.sort_bytakesrisk_score(the default),created_atorid.sort_ordertakesdesc(the default) orasc. Ties are broken byid.created_fromandcreated_to(ISO 8601) keep findings saved in that window.
What each finding gives you
data: the finding itself. The fields depend on the source. Every source giveskeyword,urlandsnippet.github:repo,path,title,language,file_sizeandcommit_date(anywhen you set nosinceoruntil).gitlab:projectandpath.gist:gist_id,username,filenameanddescription.google:title,site(the site searched),platformandcontent_type.postman:collection_name,usernameanddescription.
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_scoreanddata.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, with0and an empty text when no assessment ran. Google findings carry them only when one ran. The dashboard showsai_rationaleas the risk explanation.data.patterns_matched: the secret patterns found in the snippet, each with aruleand adescription. On Postman, theapi-relatedrule 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 asREADME.md, shares aresult_hashand afirst_seen_at. Comparedata.projectas 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:newon every finding.
Export the findings
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.
- The file is named
job_<scan id>_results.<format>in theContent-Dispositionheader. - Findings are in the order they were saved, not by risk.
- Each record has
id,moduleandcreated_at, then thedatafields flattened into dotted keys such aspatterns_matched[0].rule.moduleholds the scanner’s name for the source, such asgithub_search. - Every
datavalue is written as a string, in all three formats. - In XML,
id,moduleandcreated_atare attributes of eachresultelement. In keys, dots and opening brackets become underscores and closing brackets are dropped, sopatterns_matched[0].rulebecomespatterns_matched_0_rule. - A scan with no findings returns
404with{"detail": "No results found for this job"}.
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/ lists your organization’s keyword scans, newest first, including the ones started in the dashboard.
pagestarts at 1.per_pagedefaults to 10, max 100.statusfilters on one status, such asrunning.sourcefilters on one source.created_fromandcreated_to(ISO 8601) keep scans created in that window.- The page has the same envelope as the results:
total,page,per_page,total_pagesanditems.
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.
Find contributors
A contributor scan lists the developers behind an organization’s public repositories. Use their user names asusers 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/ takes custom_links: a list of full URLs.- 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.
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: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}/ until status is SUCCESS or FAILURE. Contributor statuses are uppercase:PENDING: waiting to run.RUNNING: scanning.progressmoves from 0 to 100 andmessagesays what the scan is doing.SUCCESS: done. Readresult.FAILURE: done, with the reason inerror.
3
Read the contributors
On
SUCCESS, result lists the developers found:unique_contributors: one entry per developer, merged across all links. On GitHub,repo_countcounts 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_urlisnull, andusernameis 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:falsewhen the scan found nobody. The status is stillSUCCESS.
result also carries link_results, with the detail for each link.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/ takes three fields:
template_name: required. Names are unique within your organization.template_type:scan(the default) orreport.settings: required. Any JSON object, stored as you send it.
201 with the template’s id, template_name, template_type, settings, created_at and updated_at. 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. 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
queuedorrunningat once, dashboard scans included. Each source in a start request is one scan. A source over the limit lands infailureswithstatus_code429, and its keywords are still counted. To avoid that, list yourqueuedandrunningscans first and keep theirtotalplus 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
failureswith429and anerrorfield that starts withRate limit exceeded. A contributor start over it returns that429and 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
capexplains. Change them with the source options. - Trailing slash. Every path ends with
/. A path without it returns a301redirect, and aPOSTorDELETEsent 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
Errors explains every body the API returns.