API overview
Authenticate with an API key, submit a scan, read its verdict, and manage policies and baselines from your own automation.
Everything the dashboard and the CLI do goes through the same HTTP API, so anything you can do by hand you can do from a script. Requests and responses are JSON.
Authentication
Send an API key from the dashboard's API keys page as a bearer token:
Authorization: Bearer pkf_live_...
A key belongs to the account it was created in, can be given an expiry, and can be revoked at any time. Its last use is shown in the dashboard, so you can see which keys are alive. Keys start with pkf_live_, which makes them easy to find in logs and for secret scanners to recognise.
Submitting a scan
curl -X POST https://pkgfire.org/api/v1/scans \
-H "Authorization: Bearer $PKGFIRE_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"ecosystem": "npm",
"project": "example/app",
"packages": [{"name": "lodash", "version": "4.17.20"}]
}'
Scans are asynchronous. The answer is 202 with the scan's id; poll GET /api/v1/scans/{id} until status is completed, failed or cancelled, then read verdict. A scan that did not complete has no verdict, and is never an ALLOW. A scan may hold up to 5,000 packages.
Before submitting, POST /api/v1/credits/quote with {"packages": N} says the most a scan of that size can cost. Sending X-Max-Credits: N with the scan refuses it if it would cost more.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/scans | Submit a dependency set for checking |
GET | /api/v1/scans | Scan history; ?project=<slug> narrows it to one project |
GET | /api/v1/scans/{id} | One scan: status, verdict, reasons and findings |
GET | /api/v1/scans/{id}/findings | Just the findings |
GET | /api/v1/scans/{id}/sbom | A software bill of materials of a completed scan, CycloneDX JSON; ?format=spdx for SPDX |
POST | /api/v1/packages/scan | Check one package version |
GET | /api/v1/packages/{ecosystem}/{name}/{version} | The stored verdict for a version your account has checked |
GET | /api/v1/findings | Findings across scans |
GET | /api/v1/projects | Projects |
GET PUT | /api/v1/policy | The account policy |
GET PUT DELETE | /api/v1/projects/{slug}/policy | A project's own policy: read (it says whether the project inherits), set, remove |
GET POST DELETE | /api/v1/projects/{slug}/baseline | Accepted findings: list, accept a scan's findings, clear |
DELETE | /api/v1/projects/{slug}/baseline/{id} | Remove one accepted finding |
GET | /api/v1/projects/{slug}/licenses | Licence report of the project's latest completed scan |
GET | /api/v1/credits | Balance and recent credit activity |
POST | /api/v1/credits/quote | The most a scan of a given size can cost |
GET | /api/v1/activity | The account's audit trail, newest first |
Adding alert destinations, connecting a repository and buying credits need a signed-in session in the dashboard; an API key, which a CI job holds, cannot do them.
Errors
An error is a status code with a JSON body:
{"error": "not_found", "message": "No such scan."}
error is a stable code to act on; message is written for a person.
Rate limits
Requests are limited per credential, the API key or the signed-in session, never per address, so the projects behind one CI egress address are not throttled together. Every refusal carries Retry-After, and ordinary answers carry RateLimit-Limit and RateLimit-Remaining.
| Answer | Meaning |
|---|---|
429 rate_limited | Too many requests this minute with this credential |
429 scan_rate_limited | Too many scans this hour on this account |
429 too_many_active_scans | Too many scans queued or running on this account at once |
503 queue_full | The service cannot take more work right now. Not your fault: retry after the time the header gives |
The limits are generous enough that the CLI polling a running scan never meets them.