Vulnerability & Dependency Intelligence API
The Vulnerability & Dependency Intelligence API returns OSV.dev-powered vulnerability data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary package_vulns endpoint returns a package's known vulnerabilities with a count and a severity summary (critical, high, moderate, low), and you can pull a vuln_detail, search advisories, check the CISA KEV (known-exploited) list, look up exploits and batch many packages. It is built for supply-chain security, dependency review and patch-prioritization workflows that need actionable vulnerability intelligence without stitching feeds together. One ReefAPI key, one shared credit pool, the standard envelope.
ID formats, ecosystem vocabularies, and where each number comes from
Three different databases sit behind this API and they do not agree on spelling or on scoring. package_vulns speaks OSV ecosystem names, advisory_search speaks GitHub's own tokens, and the numeric CVSS score only exists on the GitHub side. The table below is the vocabulary you need to get a hit on the first try.
| Input or field | Accepted format | Measured behavior |
|---|---|---|
| id, for vuln_detail | GHSA-xxxx-xxxx-xxxx, CVE-YYYY-NNNNN, PYSEC-, GO- or RUSTSEC- | case-insensitive, and a CVE is resolved through OSV's alias index; an id with no record returns NOT_FOUND rather than an empty object |
| ecosystem, for package_vulns | 19 tokens: alpine, android, bitnami, cargo, composer, crates.io, debian, githubactions, go, hex, maven, npm, nuget, packagist, pub, pypi, rubygems, swifturl, ubuntu | aliases are normalized in the response, so ecosystem cargo comes back as crates.io; an unlisted value such as javascript is rejected with INVALID_PARAM and the allowed list |
| ecosystem, for advisory_search | 13 GitHub tokens: actions, composer, erlang, go, maven, npm, nuget, other, pip, pub, rubygems, rust, swift | note pip rather than pypi and rust rather than cargo; the two actions do not share a vocabulary |
| Maven package name | group:artifact | org.apache.logging.log4j:log4j-core, not log4j-core on its own |
| severity_label | CRITICAL, HIGH, MODERATE, LOW | uppercase on the vulnerability record, while severity_summary.by_severity uses lowercase keys plus an unknown bucket for advisories with no rating |
| cvss_type | CVSS_V3 or CVSS_V4 | v4 vectors begin CVSS:4.0/AV:N/AC:L/AT:N/ and v3 vectors begin CVSS:3.1/AV:N/AC:L/PR:N/ |
| cvss_base_score | a number or null | null in package_vulns even when a vector is present; the number arrives through the ghsa block on vuln_detail, which returned 10.0 for Log4Shell |
| epss_percentage and epss_percentile | 0 to 1, a probability rather than a percent | CVE-2021-44228 measured 0.99999 and 1.0 despite the field name saying percentage |
| KEV catalog | catalog_version as a date string, catalog_count as an integer | measured 2026.08.26 with 1682 entries; known_ransomware_campaign_use is the string Known or Unknown, not a boolean |
CPE strings are not a first-class field. A CVE-level record exposes them under database_specific.unresolved_ranges as CPE_RANGE entries, and CVE-2021-44228 returned 150 of them, including cpe:2.3:a:apple:xcode:*:*:*:*:*:*:*:* with a fixed event of 13.3. Package-level matching goes through affected[].purl instead, for example pkg:maven/org.apache.logging.log4j/log4j-core.
Real request and response JSON
Captured from the indexed primary action, package_vulns, on .
{
"method": "POST",
"url": "https://api.reefapi.com/vuln-intel/v1/package_vulns",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"package": "org.apache.logging.log4j:log4j-core",
"ecosystem": "Maven",
"version": "2.14.1"
}
}{
"ok": true,
"meta": {
"api": "vuln-intel",
"endpoint": "package_vulns",
"mode": "live",
"latency_ms": 1017.1,
"record_count": 7,
"bytes": 53027,
"cache_hit": false,
"query_kind": "package",
"highest_severity": "critical"
},
"data": {
"package": "org.apache.logging.log4j:log4j-core",
"ecosystem": "Maven",
"version": "2.14.1",
"commit": null,
"vuln_count": 7,
"severity_summary": {
"by_severity": {
"critical": 2,
"high": 1,
"moderate": 4,
"low": 0,
"unknown": 0
},
"highest_severity": "critical",
"total": 7
},
"vulnerabilities": [
{
"id": "GHSA-3pxv-7cmr-fjr4",
"aliases": [
"CVE-[redacted-phone]"
],
"cve_ids": [
"CVE-[redacted-phone]"
],
"summary": "Apache Log4j Core: Silent log event loss in XmlLayout due to unescaped XML 1.0 forbidden characters",
"details": "Apache Log4j Core's [`XmlLayout`](https://logging.apache.org/log4j/2.x/manual/layouts.html#XmlLayout), in versions up to and including 2.25.3, fails to sanitize characters forbidden by the [XML 1.0 specification](https://www.w3.org/TR/xml/#charsets), producing invalid XML output whenever a log message or MDC value contains such characters.\n\nThe impact depends on the StAX implementation in use:\n\n * **JRE built-in StAX**: Forbidden characters are silently written to the output, producing malformed XML. Conforming parsers must reject such documents with a fatal error, which may cause downstream",
"severity_label": "MODERATE",
"cvss_vector": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:L/SA:N",
"cvss_type": "CVSS_V4",
"cvss_base_score": null,
"published": "[redacted-phone]T18:31:17Z",
"modified": "[redacted-phone]T11:29:[redacted-phone]Z",
"withdrawn": null,
"related": [
"CGA-wqhw-vh22-jwgm"
],
"affected": [
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
},
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
}
],
"fixed_versions": [
"2.25.4"
],
"references": [
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"database_specific": {
"nvd_published_at": "[redacted-phone]T16:16:31Z",
"severity": "MODERATE",
"cwe_ids": [
"[trimmed-depth]"
],
"github_reviewed_at": "[redacted-phone]T21:16:41Z",
"github_reviewed": true
},
"schema_version": "1.7.5",
"source": "osv.dev"
},
{
"id": "GHSA-6hg6-v5c8-fphq",
"aliases": [
"CVE-[redacted-phone]"
],
"cve_ids": [
"CVE-[redacted-phone]"
],
"summary": "Apache Log4j Core: `verifyHostName` attribute silently ignored in TLS configuration",
"details": "The fix for CVE-[redacted-phone] was incomplete: it addressed hostname verification only when enabled via the [`log4j2.sslVerifyHostName`](https://logging.apache.org/log4j/2.x/manual/systemproperties.html#log4j2.sslVerifyHostName) system property, but not when configured through the [`verifyHostName`](https://logging.apache.org/log4j/2.x/manual/appenders/network.html#SslConfiguration-attr-verifyHostName) attribute of the `<Ssl>` element.\n\nAlthough the `verifyHostName` configuration attribute was introduced in Log4j Core 2.12.0, it was silently ignored in all versions through 2.25.3, leaving ",
"severity_label": "MODERATE",
"cvss_vector": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:L/SA:N",
"cvss_type": "CVSS_V4",
"cvss_base_score": null,
"published": "[redacted-phone]T18:31:17Z",
"modified": "[redacted-phone]T12:29:[redacted-phone]Z",
"withdrawn": null,
"related": [
"CGA-99gg-5485-cvhj"
],
"affected": [
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
},
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
}
],
"fixed_versions": [
"2.25.4"
],
"references": [
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"database_specific": {
"github_reviewed_at": "[redacted-phone]T00:11:55Z",
"github_reviewed": true,
"nvd_published_at": "[redacted-phone]T16:16:30Z",
"severity": "MODERATE",
"cwe_ids": [
"[trimmed-depth]"
]
},
"schema_version": "1.7.5",
"source": "osv.dev"
},
{
"id": "GHSA-7rjr-3q55-vv33",
"aliases": [
"CVE-[redacted-phone]"
],
"cve_ids": [
"CVE-[redacted-phone]"
],
"summary": "Incomplete fix for Apache Log4j vulnerability",
"details": "# Impact\n\nThe fix to address [CVE-[redacted-phone]](https://nvd.nist.gov/vuln/detail/CVE-[redacted-phone]) in Apache Log4j 2.15.0 was incomplete in certain non-default configurations. This could allow attackers with control over Thread Context Map (MDC) input data when the logging configuration uses a non-default Pattern Layout with either a Context Lookup (for example, $${ctx:loginId}) or a Thread Context Map pattern (%X, %mdc, or %MDC) to craft malicious input data using a JNDI Lookup pattern resulting in a remote code execution (RCE) attack. \n\n## Affected packages\nOnly the `org.apache.loggi",
"severity_label": "CRITICAL",
"cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H/E:H",
"cvss_type": "CVSS_V3",
"cvss_base_score": null,
"published": "[redacted-phone]T18:01:28Z",
"modified": "[redacted-phone]T19:37:[redacted-phone]Z",
"withdrawn": null,
"related": [],
"affected": [
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
},
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
},
{
"package": "[trimmed-depth]",
"ecosystem": "[trimmed-depth]",
"purl": "[trimmed-depth]",
"introduced": "[trimmed-depth]",
"fixed": "[trimmed-depth]",
"last_affected": "[trimmed-depth]",
"versions_listed": "[trimmed-depth]"
}
],
"fixed_versions": [
"2.16.0",
"2.12.2",
"1.9.2"
],
"references": [
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"database_specific": {
"nvd_published_at": "[redacted-phone]T19:15:00Z",
"severity": "CRITICAL",
"cwe_ids": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"github_reviewed_at": "[redacted-phone]T17:55:00Z",
"github_reviewed": true
},
"schema_version": "1.7.3",
"source": "osv.dev"
}
]
}
}What the Vulnerability & Dependency Intelligence API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| package_vulns | All known vulnerabilities affecting a package (by name+ecosystem, optionally pinned to an installed version) OR a git commit — via OSV.dev. The dependency-audit core. | Platform and DevOps teams call package_vulns to get all known vulnerabilities affecting a package (by name+ecosystem, optionally pinned to an ins…. | package, ecosystem, version, commit |
| vuln_detail | Full detail for one vulnerability by OSV id, GHSA id, or CVE id. OSV is the base record; if it resolves to a GHSA the response is enriched with GitHub's numeric CVSS base score, EPSS exploitation probability, and CWEs. | Security and supply-chain teams call vuln_detail to get full detail for one vulnerability by OSV id, GHSA id, or CVE id. | id |
| advisory_search | Search the GitHub Advisory Database (GHSA) by ecosystem, severity, CVE, free text, type. Cursor-paginated (meta.next_cursor). | Developer-tool builders call advisory_search to search the GitHub Advisory Database (GHSA) by ecosystem, severity, CVE, free text, type. | ecosystem, severity, cve_id, query, type, ... |
| kev_check | Is a CVE in the CISA Known-Exploited-Vulnerabilities catalog (actively exploited in the wild)? Returns the KEV entry when listed. | AI-agent developers call kev_check to get is a CVE in the CISA Known-Exploited-Vulnerabilities catalog (actively exploited in the wild)?. | cve_id |
| kev_list | Browse / filter the full CISA KEV catalog by vendor, product, or ransomware flag, with page pagination. | Platform and DevOps teams call kev_list to get browse / filter the full CISA KEV catalog by vendor, product, or ransomware flag, with page p…. | vendor, product, ransomware, page, per_page |
| exploits | Public exploit / PoC lookup for a CVE via the Exploit-DB (Offensive Security) database (keyless, CVE-indexed). Answers 'is there a weaponized public exploit?' — has_public_exploit + each entry's edb_id, type (remote/local/webapps/dos), platform, verified flag, date_published, and deep links (exploit_db_url + upstream source_url). | Security and supply-chain teams call exploits to get public exploit / PoC lookup for a CVE via the Exploit-DB (Offensive Security) database (keyle…. | cve_id, verified_only |
| batch | Bulk-scan up to 100 package/commit queries in one call (OSV querybatch), each hydrated to full vulnerability detail by default. | Developer-tool builders call batch to get bulk-scan up to 100 package/commit queries in one call (OSV querybatch), each hydrated to ful…. | queries, hydrate |
Call package_vulns from your stack
curl -X POST https://api.reefapi.com/vuln-intel/v1/package_vulns \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"package":"org.apache.logging.log4j:log4j-core","ecosystem":"Maven","version":"2.14.1"}'import requests
r = requests.post(
"https://api.reefapi.com/vuln-intel/v1/package_vulns",
headers={"x-api-key": REEF_KEY},
json={
"package": "org.apache.logging.log4j:log4j-core",
"ecosystem": "Maven",
"version": "2.14.1"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/vuln-intel/v1/package_vulns", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"package": "org.apache.logging.log4j:log4j-core",
"ecosystem": "Maven",
"version": "2.14.1"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.vuln-intel.package_vulns with {"package":"org.apache.logging.log4j:log4j-core","ecosystem":"Maven","version":"2.14.1"}.Who uses this API and why
- Security teams call package_vulns to find and rank vulnerabilities across a project's dependencies.
- Patch-prioritization tools use kev_check to flag dependencies with known-exploited CVEs first.
- Compliance workflows use advisory_search and vuln_detail to document and track remediation.
Questions developers ask before integrating
Why is cvss_base_score null when the vector is right there?
Because package_vulns returns the raw OSV record, which carries the vector string but not a computed base score. A measured lodash advisory had cvss_vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L with cvss_base_score null, and a log4j-core advisory had a CVSS:4.0 vector with the same null. The number comes from GitHub's enrichment, which only vuln_detail performs. Sort a dependency audit on severity_label, then call vuln_detail on the ones you need a number for.
Does querying by CVE give the same record as querying by GHSA?
No, and the difference matters. vuln_detail for CVE-2021-44228 returned the CVE-level OSV record with summary null, affected[0].package, ecosystem and purl all null, and version ranges expressed as git commit hashes such as 38513a7d57343881f7bf58f37e67d6a87e0a47c5. The same call for GHSA-jfh8-c2jp-5v3q returned package org.apache.logging.log4j:log4j-core, ecosystem Maven, introduced 2.13.0 and fixed 2.15.0. Both responses carry the ghsa block with the readable summary, so use the GHSA id when you want package version ranges.
What does a package with no known vulnerabilities look like?
ok true with vuln_count 0, an empty vulnerabilities array, meta.record_count 0 and severity_summary.highest_severity null rather than the string none, with every by_severity bucket at 0. Measured on left-pad 1.3.0 in npm. There is no 404 for a clean package, so branch on vuln_count and treat highest_severity null as the clean marker.
Does pinning a version really change the answer?
Yes. log4j-core 2.14.1 in Maven returned 7 vulnerabilities with by_severity critical 2, high 1 and moderate 4, and highest_severity critical. Omitting version returns every vulnerability the package has ever had, which is the wrong list for an audit. A current release is not automatically clean either: lodash 4.17.21 still returned 3 open advisories with highest_severity high.
KEV, EPSS and Exploit-DB all sound like the same thing. What is the difference?
They are three separate claims. kev_check is CISA's binary in_kev flag with a date_added, a due_date for US federal agencies and a known_ransomware_campaign_use string. EPSS is a modeled probability between 0 and 1 that the CVE will be exploited. exploits queries Exploit-DB for code that actually exists. Log4Shell scored on all three: in_kev true added 2021-12-10 with ransomware Known, epss 0.99999, and exploit_count 3 including edb_id 50592. A CVE can sit in KEV with zero Exploit-DB rows, and the reverse happens too.
How is exploit_references different from the exploits action?
exploit_references is extracted from the advisory's own reference list by matching known PoC hosts such as exploit-db, packetstorm and metasploit, so it is only as complete as that advisory. meta.exploit_ref_count read 19 for the CVE record and 26 for the GHSA record of the same vulnerability, because the two records list different references. The exploits action queries Exploit-DB directly and returns edb_id, type (remote, local, webapps, dos), platform, author, date_published, a verified flag and both exploit_db_url and source_url. All three Log4Shell rows came back verified false.
How does pagination work, and is there a rate limit I can see?
The two list actions paginate differently. advisory_search is cursor-based: meta.next_cursor holds an opaque base64 string you pass back as cursor, alongside has_more. kev_list is page-based with meta.page, per_page, has_more and next_page, and a filtered call for Microsoft entries flagged for ransomware reported total 114 against catalog_count 1682. advisory_search also reports rate_limit_remaining against rate_limit 5000 on every response.
How do I scan a whole lockfile in one call?
batch accepts up to 100 query objects, each either package plus ecosystem plus optional version, or a commit hash, and preserves input order in results. hydrate defaults to true and expands every hit to the full normalized record; set it false when you only need the id and modified date per hit and want the call to return faster. meta.record_count is the total vulnerability count across all queries, not the number of queries.
What is the Vulnerability & Dependency Intelligence API?
Vulnerability & Dependency Intelligence API is a ReefAPI endpoint group for vulnerability & dependency intelligence It returns live JSON through POST requests under /vuln-intel/v1.
Is the Vulnerability & Dependency Intelligence API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Vulnerability & Dependency Intelligence calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Vulnerability & Dependency Intelligence login or account?
No login to Vulnerability & Dependency Intelligence is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Vulnerability & Dependency Intelligence data?
The page example is captured from a live package_vulns call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Vulnerability & Dependency Intelligence API use?
Vulnerability & Dependency Intelligence actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Vulnerability & Dependency Intelligence from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call vuln-intel actions with the same key, credit pool and JSON envelope used by normal REST requests.