Package Vulnerabilities Scanner - OSV (npm, PyPI, Maven, Go)
Pricing
$0.50 / 1,000 package checkeds
Package Vulnerabilities Scanner - OSV (npm, PyPI, Maven, Go)
Check open source packages or a lockfile against the OSV vulnerability database. One result per package: known vulnerabilities with CVE/GHSA ids, severity, fixed versions and a recommended upgrade. Free of API keys.
Pricing
$0.50 / 1,000 package checkeds
Rating
0.0
(0)
Developer
Francesco Antonio Russo
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Package Vulnerabilities Scanner for npm, PyPI, Maven, Go and more (OSV)
Check open source packages, or a whole lockfile, against the OSV vulnerability database and get one clean result per package: the known vulnerabilities (CVE and GHSA ids, severity with a real CVSS score, affected and fixed versions), a recommended upgrade version, or an explicit "no known vulnerabilities". No API key, no account, no scraping.
For: developers and security teams who want a dependency audit as an API call or a scheduled run, agencies checking client projects, and anyone who needs the vulnerability list of a package-lock.json, requirements.txt, go.mod, Cargo.lock, Gemfile.lock, composer.lock... without installing anything.
What you get
- 12 ecosystems: npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist (PHP), Pub (Dart), Hex (Elixir), Hackage (Haskell), CRAN (R).
- 11 file formats read for you:
package-lock.json(andnpm-shrinkwrap.json),package.json,yarn.lock(v1 and Berry),pnpm-lock.yaml,requirements.txt,poetry.lock,Pipfile.lock,go.mod,Cargo.lock,Gemfile.lock,composer.lock. Give a public URL or paste the text. - Distinct vulnerabilities, not duplicates. The same vulnerability is often published three times (GitHub, PyPI, NVD). They are merged into one entry; the other records are listed in
alsoPublishedAs. Django 3.2.0 has 38 vulnerabilities, not the 66 advisory records. - A severity you can sort by. The CVSS score is computed from the advisory's vector (v3.0, v3.1, v4.0 and v2), so
cvssScoreis a number, not a label copied from somewhere. No vector and no label from the database meansnull, never a guess. fixedVersion: the lowest version that is outside every affected range of the advisories shown (when it can be calculated exactly, and it says why when it cannot).- Honest errors, free. A range like
^1.2, a missing version, an unknown ecosystem or an unreadable file gives a record that tells you what to fix. You are never charged for those.
Input
| Field | What it does |
|---|---|
packages | Packages with the exact version: {"ecosystem":"npm","name":"lodash","version":"4.17.15"} or a line such as npm lodash 4.17.15, PyPI django==3.2.0, Maven org.apache.logging.log4j:log4j-core 2.14.1, pkg:cargo/time@0.1.43. |
manifestUrls | Public URLs of dependency files. GitHub and GitLab "blob" links are turned into raw files. |
manifestText | The content of a dependency file pasted as text (up to 8 MB). |
manifestFormat | Only needed if the format cannot be detected. |
ecosystems | Check only these ecosystems (for a repo with several languages). |
minSeverity | low, medium, high or critical. Advisories with no severity at all are kept (they cannot be shown to be low). |
onlyVulnerable | Leave out packages with nothing to show: not delivered, not charged. |
includeDevDependencies | Turn off to check only what ships to production. |
includeDetails | Add the long advisory text (first 4,000 characters). |
includeWithdrawn | OSV already omits withdrawn advisories; this keeps one that was withdrawn between the lookup and the read. |
dedupePackages | On by default: a package found many times is checked and charged once, and lists where it was found. |
maxPackages | 1 to 20,000 different packages per run (default 1,000). Packages above the limit are not checked and not charged. |
Example input:
{"packages": [{ "ecosystem": "npm", "name": "lodash", "version": "4.17.15" },"PyPI django==3.2.0","Maven org.apache.logging.log4j:log4j-core 2.14.1"],"manifestUrls": ["https://github.com/mochajs/mocha/blob/v8.0.0/package-lock.json"],"minSeverity": "high","includeDevDependencies": false}
Use exact versions. A lockfile has them; package.json and requirements.txt only do for exact pins. For a range such as ^4.17.0 the Actor cannot know which version you run, so it does not guess: you get a free invalid-version record that names the lockfile to use. (This is deliberate: OSV itself answers a version it cannot read with every vulnerability ever reported for the package.)
Output
One dataset record per package. Real output of a run (shortened to one of the six vulnerabilities):
{"ecosystem": "npm","name": "lodash","version": "4.17.15","purl": "pkg:npm/lodash@4.17.15","status": "vulnerable","error": null,"vulnerabilityCount": 6,"maxSeverity": "high","maxCvssScore": 8.1,"severityCounts": { "critical": 0, "high": 3, "medium": 3, "low": 0, "unknown": 0 },"fixedVersion": "4.18.0","fixedVersionStatus": "calculated","vulnerabilities": [{"id": "GHSA-p6mc-m468-83gw","aliases": ["CVE-2020-8203"],"cveIds": ["CVE-2020-8203"],"summary": "Prototype Pollution in lodash","severity": "high","cvssScore": 7.4,"cvssVector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H","severitySource": "cvss-3.1","published": "2020-07-15T19:15:48Z","modified": "2025-08-12T21:56:17.174859Z","fixedVersions": ["4.17.19"],"affectedRanges": [{ "type": "SEMVER", "introduced": "3.7.0", "fixed": "4.17.19", "lastAffected": null }],"weaknesses": ["CWE-1321", "CWE-770"],"advisoryUrl": "https://nvd.nist.gov/vuln/detail/CVE-2020-8203","osvUrl": "https://osv.dev/vulnerability/GHSA-p6mc-m468-83gw","database": { "name": "GitHub Advisory Database", "license": "CC-BY-4.0", "attribution": "GitHub Advisory Database (CC-BY-4.0), via OSV.dev" },"alsoPublishedAs": []}],"dependencyScope": null,"foundIn": ["package list"],"checkedAt": "2026-10-19T17:23:06.074Z","dataSource": "OSV.dev"}
A package that got an answer has status vulnerable, no-known-vulnerabilities or only-below-minimum-severity (it has advisories, all under the minSeverity you set; it is not "clean"). These are the charged results. Everything else is a free record that says what went wrong:
{ "ecosystem": "PyPI", "name": "requests", "version": null, "versionInput": null, "status": "invalid-version","error": "requirements.txt has \">=2.0\" instead of an exact pin (==): pin the version, or use a lock file", "foundIn": ["pasted manifest"] }
Other free statuses: invalid-name, unsupported-ecosystem, lookup-failed (OSV did not answer for that package), manifest-error (file not found, private, not a lockfile), not-checked (a limit was reached). Every field is always present, null when the source does not say.
The run also writes a SUMMARY record (key-value store): counts per status and severity, per-file results (format, packages read, entries skipped with the reason), request counts, warnings and why a run stopped early.
| Main fields | Meaning |
|---|---|
version, purl | The exact version checked and its Package URL (join with an SBOM). |
vulnerabilityCount, maxSeverity, maxCvssScore, severityCounts | Totals for sorting and filtering. |
fixedVersion, fixedVersionStatus, fixedVersionNote | Recommended upgrade; status is calculated, not-needed, no-fix-published (an advisory lists no fix for your release line) or not-calculable. |
vulnerabilities[] | id, aliases, cveIds, summary, severity, cvssScore, cvssVector, cvssVersion, severitySource, published, modified, fixedVersions, affectedRanges, weaknesses (CWE), links, database (name, license, attribution), alsoPublishedAs. |
dependencyScope | prod or dev when the lockfile says so. |
foundIn, notes | Where the package came from; things worth knowing about this result. |
Price
$0.0005 per package checked ($0.50 per 1,000 packages), one event, package-checked. You pay only for packages that got an answer (vulnerable, clean, or only below your severity filter). Errors, invalid input, unreadable files and packages left out by a limit are free. Set a maximum cost per run in Apify and the Actor stops cleanly when it is reached.
Examples (measured on real runs): a 3-package list costs $0.0015. The 2,800 packages of the yarn.lock of Next.js 12 were checked in 22 seconds: $1.40 if every package is delivered, $0.10 with onlyVulnerable and minSeverity: critical (195 packages had something to show). With onlyVulnerable you pay only for the packages that have something to show.
Where the data comes from, and licenses
Data comes from OSV.dev, the open vulnerability database run by Google, which aggregates advisory databases. This Actor is not affiliated with OSV or Google. OSV documents its API as not rate limited and publishes the licenses of each source on its data sources page, read on 2026-10-19: GitHub Advisory Database, PyPI Advisory Database, Go Vulnerability Database, OSS-Fuzz and the Erlang Ecosystem Foundation CNA are CC-BY 4.0; RustSec, the Global Security Database and Haskell Security Advisories are CC0; OpenSSF Malicious Packages, R Consortium and Bitnami are Apache 2.0; Drupal is MIT. None of them restricts commercial use.
Every vulnerability carries its database with name, license and an attribution line, and merged duplicates are listed in alsoPublishedAs with their own license. If you reuse CC-BY advisories, keep that attribution. The severity scores and fixedVersion are computed by this Actor from the advisory data and are not part of the original records. A record from a database that OSV does not list with a license (for example OSV's own conversion of NVD entries) has license: null and a note saying so; the Actor does not invent one.
FAQ
Does it tell me whether my code actually uses the vulnerable function? No. It lists the known vulnerabilities of the exact versions you have. Reachability analysis needs your source code.
Why is fixedVersion sometimes null? Because it would be a guess otherwise. no-fix-published: an advisory has no fixed version for your release line (common for end-of-life versions). not-calculable: the versions use qualifiers (rc, beta, SNAPSHOT) whose order depends on the ecosystem; the fixed versions of each advisory are still listed. When the only fix is in a newer major version, the note says so.
Why does the version have to be exact? See the Input section. Resolving a range to an installed version needs a lockfile or a registry, and a guess would give a false "clean".
Are names case-sensitive? PyPI and Packagist names are normalised for you. npm names are matched as written. OSV matches NuGet names with their exact capitalisation (Newtonsoft.Json, not newtonsoft.json), so an all-lowercase NuGet name that finds nothing is looked up once in the public NuGet registry (api.nuget.org) for its exact spelling and checked again; the record says the name was corrected. If the registry does not answer or does not know the name, the result carries a note instead.
Which vulnerabilities are included? Those OSV matches to the exact package and version, from every database it aggregates, withdrawn ones excluded. A "no known vulnerabilities" result means OSV has no advisory for that version today; it is not a guarantee of safety.
Private files? Only public URLs are fetched (ports 80 and 443, no addresses inside private networks, no URLs with a password, files up to 8 MB). For private repos paste the text in manifestText. Only the one URL you give is downloaded; no links are followed. file:, link:, workspace: and git/URL dependencies inside a lockfile are skipped and counted in the SUMMARY.
What is not supported? pom.xml, build.gradle, container images, SBOM files and OS packages (Debian, Alpine, Ubuntu). Git and local-path dependencies are skipped and listed in the SUMMARY with the reason.
How big can it be? Up to 20,000 different packages and 8 MB per file per run. Very long lists of problems are summarised in one free record instead of thousands.
Integration
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('far003/package-vulnerabilities-osv').call({manifestUrls: ['https://github.com/mochajs/mocha/blob/v8.0.0/package-lock.json'],minSeverity: 'high',onlyVulnerable: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const pkg of items) console.log(pkg.name, pkg.version, pkg.maxSeverity, '->', pkg.fixedVersion);
Python:
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("far003/package-vulnerabilities-osv").call(run_input={"packages": ["PyPI django==3.2.0", "npm lodash 4.17.15"],})for pkg in client.dataset(run["defaultDatasetId"]).iterate_items():print(pkg["name"], pkg["version"], pkg["status"], pkg["maxSeverity"], pkg["fixedVersion"])
Run it on a schedule against the lockfile URL of each project and use onlyVulnerable with minSeverity: "high" to get a record only when something needs attention.