API Quickstart

Submit your first scan via the REST API — generate an SBOM, queue a scan, poll it, and read the verdicts.

This guide walks through the core API flow end to end: generate an SBOM, submit it for scanning, poll until the scan finishes, and read the verdicts. Everything here uses plain curl.

📘

Before you start

You need an API key. Create one at dashboard.ossprey.com (Account → API Keys), then export it:

export OSSPREY_API_KEY="ospy_..."

Keys are shown once, at creation, and must be given an expiry date (up to 2 years). See Security & API Keys.


Step 1 — Build an SBOM

A scan takes an SBOM in Ossprey's OSSBOM format: a format marker plus a list of components identified by Package URL (purl). You can build one yourself from any manifest:

{
  "format": "OSSBOM",
  "components": [
    { "purl": "pkg:npm/[email protected]", "name": "lodash", "version": "4.17.21", "type": "npm" },
    { "purl": "pkg:pypi/[email protected]", "name": "requests", "version": "2.31.0", "type": "pypi" }
  ]
}

Several versions of the same package can go in one request — list each as its own component.

Or let the API generate one from a GitHub repository:

curl -X POST "https://api.ossprey.com/public/v1/tools/sbom?purl=pkg:github/expressjs/express" \
  -H "x-api-key: $OSSPREY_API_KEY"

Only pkg:github/owner/repo[@ref][?path=...] purls are supported by the generator today. (The CLI does the same thing for local projects.)

Step 2 — Submit the scan

curl -X POST "https://api.ossprey.com/public/v1/scans" \
  -H "x-api-key: $OSSPREY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sbom": {
      "format": "OSSBOM",
      "components": [
        { "purl": "pkg:npm/[email protected]", "name": "lodash", "version": "4.17.21", "type": "npm" }
      ]
    }
  }'

A successful submission returns 202 with the ids you'll need to poll:

{ "sbom_id": "…", "scan_id": "…", "status": "QUEUED" }

Notes:

  • An SBOM with no components returns 200 immediately — accepted, but nothing to scan.
  • The optional settings object supports ignore_cache and ignore_kb (both default false) to force fresh analysis.
  • The 202 response carries your quota state in X-Quota-* headers; a 429 means your daily or monthly package quota is exhausted.

Step 3 — Poll for completion

curl "https://api.ossprey.com/public/v1/scans/status?sbom_id=$SBOM_ID&scan_id=$SCAN_ID" \
  -H "x-api-key: $OSSPREY_API_KEY"
  • 202 — still going: { "status": "QUEUED" | "PENDING" | "RUNNING", "output": "…" }
  • 200 — finished: { "status": "SUCCEEDED", "output": { …full SBOM with vulnerabilities… } }
  • 400 — the scan failed (or ids were missing)

Poll on an interval until you get 200 or 400. (POST to the same path behaves identically, for clients that can't easily GET with query parameters.)

Step 4 — Read the results

The completed output SBOM (and GET /public/v1/scans/{sbom_id} any time later) lists each detected issue as a vulnerability:

{
  "id": "…",
  "purl": "pkg:npm/[email protected]",
  "description": "…",
  "status": "open",
  "threat_card": {
    "severity": "Critical",
    "threat_type": "Obfuscated code execution",
    "justification": "…why the package was flagged…",
    "code_examples": [
      {
        "code": "…",
        "filename": "index.js",
        "language": "javascript",
        "start_line": 42,
        "highlight_lines": [3, 4]
      }
    ],
    "mitre_techniques": ["T1059.007", "T1027"],
    "ossprey_ttps": ["OSP-001"]
  }
}

An empty vulnerabilities list means no malware was found. Malicious findings carry a threat_card with the severity, a human-readable justification, illustrative code excerpts, and MITRE ATT&CK / Ossprey TTP mappings — the same data the dashboard renders as a threat card.

severity is one of Info, Low, Medium, High, Critical. status carries your triage state for the finding: open, investigating, fixed, wont_fix, or false_positive. Findings you've triaged as not_malicious in the dashboard are omitted from GET /public/v1/scans/{sbom_id}.

📘

Findings, not CVEs

The field is named vulnerabilities for backwards compatibility, but Ossprey detects malicious behaviour rather than published CVEs. The product calls these findings.

Managing your scans

# List all your SBOMs with the latest scan status
curl "https://api.ossprey.com/public/v1/scans" -H "x-api-key: $OSSPREY_API_KEY"

# Delete an SBOM and its scan history
curl -X DELETE "https://api.ossprey.com/public/v1/scans/$SBOM_ID" -H "x-api-key: $OSSPREY_API_KEY"

Bonus — the malware feed

Pull every package flagged as malicious across Ossprey's package cache (not just your own scans) within a window of up to 7 days:

# Last 7 days (default)
curl "https://api.ossprey.com/public/v1/malware" -H "x-api-key: $OSSPREY_API_KEY"

# Look back N days
curl "https://api.ossprey.com/public/v1/malware?days=3" -H "x-api-key: $OSSPREY_API_KEY"

# Explicit window
curl "https://api.ossprey.com/public/v1/malware?start=2026-07-20&end=2026-07-27" \
  -H "x-api-key: $OSSPREY_API_KEY"

Use days or start/end, not both — supplying both returns 400, as does a window wider than 7 days.

Each entry includes the package URL, severity, the justification recorded at detection time, and the detection timestamp — useful for feeding your SIEM or internal block-lists.


For every parameter, schema, and status code, see the interactive API Reference on this site, generated from the OpenAPI definition.


Did this page help you?