Package & Dependency Trust API
The Package & Dependency Trust API scores open-source packages and repositories 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_trust endpoint returns a package's metadata, license (SPDX, tier and risk note), maintainers and keywords alongside a trust assessment, and you can score a repo, scan a lockfile and batch many packages. It is built for supply-chain security, dependency review and license-compliance workflows that need a trust signal for open-source code without manual auditing. One ReefAPI key, one shared credit pool, the standard envelope.
The five sub-scores, their weights, and why one goes 'unknown'
The overall trust_score is a weighted mean of five sub-scores, and the response prints the weights itself. Any sub-score that cannot be resolved returns the string "unknown" rather than a number, and is dropped from the mean instead of being counted as 0 or 100. The confidence field is what is left over: the summed weight of the sub-scores that did resolve. Every figure below is from live calls on lodash, requests, gin and facebook/react.
| Sub-score | Weight | What it needs | Measured |
|---|---|---|---|
| security | 0.30 | an OSV advisory lookup for the ecosystem | lodash pinned at 4.17.15 scored 0 (3 high, 3 moderate); requests unpinned scored 55 over 16 historical advisories |
| popularity | 0.20 | registry download counts or GitHub stars | lodash 98 from 711,291,373 monthly downloads and 61,283 stars; gin "unknown" with neither available |
| maintenance | 0.20 | release dates, push dates, cadence, contributor count | lodash 65 in rich mode and 90 in basic mode, on the same version |
| license | 0.15 | an SPDX id, read from the resolved repo license field | requests 90 permissive; lodash "unknown" even though metadata.license.value is "MIT" |
| dependency_risk | 0.15 | the direct dependency list | lodash 100 with zero runtime deps; requests 93 with 6 direct deps |
| overall.confidence | sum of resolved weights | nothing, it is arithmetic on the rows above | 1.0 for requests, 0.85 for lodash with license missing, 0.55 for a repo_trust call, 0.30 for gin |
| overall.grade | A / B / C / D / F | at least 3 of the 5 sub-scores resolved | gin returned grade "insufficient-data" with confidence 0.30 and a low_confidence_note explaining it |
meta.completeness_pct tracks the same idea from the source side rather than the score side: 100.0 for a PyPI lookup where every group matched, 80.0 for lodash, 20.0 for a Go module where only the vulnerability and Stack Overflow groups resolved. The provenance object names the reason per group, for example registry status "unsupported" with detail "packages engine serves npm/pypi only; Go gets vuln + declared-repo health".
Real request and response JSON
Captured from the indexed primary action, package_trust, on .
{
"method": "POST",
"url": "https://api.reefapi.com/enrich-package/v1/package_trust",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"ecosystem": "npm",
"package": "lodash",
"version": "4.17.15",
"mode": "rich"
}
}{
"ok": true,
"meta": {
"api": "enrich-package",
"endpoint": "package_trust",
"mode": "rich",
"latency_ms": 4864.3,
"record_count": 1,
"bytes": 0,
"cache_hit": false,
"completeness_pct": 80,
"subcalls": [
{
"api": "packages",
"action": "downloads",
"status": "ok",
"latency_ms": 691
},
{
"api": "vuln-intel",
"action": "package_vulns",
"status": "ok",
"latency_ms": 882.9
},
{
"api": "packages",
"action": "maintainer",
"status": "ok",
"latency_ms": 991.2
}
]
},
"data": {
"provenance": {
"registry": {
"status": "matched",
"engine": "packages"
},
"downloads": {
"status": "matched",
"engine": "packages"
},
"repo": {
"status": "matched",
"engine": "registry-metadata"
},
"vulnerabilities": {
"status": "matched",
"engine": "vuln-intel"
},
"repo_health": {
"status": "matched",
"engine": "github"
},
"community": {
"status": "matched",
"engine": "stackoverflow"
}
},
"generated_at": "[redacted-phone]T17:19:42Z",
"honesty": "trust score is a RISK SIGNAL, not a security guarantee; scores degrade to 'unknown' rather than show a clean-looking default when a source is missing or a repo match is low-confidence",
"package": {
"name": "lodash",
"ecosystem": "npm",
"version": "4.17.15",
"latest_version": "4.18.1",
"description": "Lodash modular utilities.",
"homepage": "https://lodash.com/"
},
"metadata": {
"license": {
"value": "MIT",
"raw": "MIT"
},
"keywords": [
"modules",
"stdlib",
"util"
],
"version_count": 117,
"latest": "4.18.1",
"maintainers": [
{
"name": "mathias",
"email": "[redacted-email]"
},
{
"name": "jdalton",
"email": "[redacted-email]"
},
{
"name": "bnjmnt4n",
"email": "[redacted-email]"
}
]
},
"downloads": {
"registry": "npm",
"period": "last-month",
"downloads": 617129388,
"start": "[redacted-phone]",
"end": "[redacted-phone]"
},
"repo": {
"found": true,
"host": "github",
"owner": "lodash",
"repo": "lodash",
"repo_url": "https://github.com/lodash/lodash",
"match": {
"confidence": 0.95,
"class": "declared-exact",
"source_field": "repository",
"name_similarity": 1,
"evidence": [
"github owner/repo parsed from package `repository` field → lodash/lodash",
"repo↔package name similarity = 1.00"
],
"reason": null
},
"health": {
"stars": 61242,
"forks": 7182,
"watchers": 803,
"open_issues": 96,
"archived": false,
"license": "NOASSERTION",
"pushed_at": "[redacted-phone]T19:48:01Z",
"created_at": "[redacted-phone]T04:11:46Z",
"default_branch": "main",
"top_languages": [
"JavaScript",
"HTML",
"EJS"
],
"release_count_listed": 4,
"last_release": "[redacted-phone]T21:01:28Z",
"releases_12mo": 2,
"contributors_listed": 30,
"bus_factor": 30,
"top_contributors": [
{
"login": "[trimmed-depth]",
"contributions": "[trimmed-depth]"
},
{
"login": "[trimmed-depth]",
"contributions": "[trimmed-depth]"
},
{
"login": "[trimmed-depth]",
"contributions": "[trimmed-depth]"
}
]
}
},
"vulnerabilities": {
"count": 6,
"highest_severity": "high",
"by_severity": {
"critical": 0,
"high": 3,
"moderate": 3,
"low": 0,
"unknown": 0
},
"items": [
{
"id": "GHSA-29mw-wpgm-hmr9",
"cve_ids": [
"[trimmed-depth]"
],
"summary": "Regular Expression Denial of Service (ReDoS) in lodash",
"severity": "MODERATE",
"cvss_base_score": null,
"fixed_versions": [
"[trimmed-depth]"
]
},
{
"id": "GHSA-35jh-r3h4-6jhm",
"cve_ids": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"summary": "Command Injection in lodash",
"severity": "HIGH",
"cvss_base_score": null,
"fixed_versions": [
"[trimmed-depth]"
]
},
{
"id": "GHSA-f23m-r3pf-42rh",
"cve_ids": [
"[trimmed-depth]",
"[trimmed-depth]"
],
"summary": "lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`",
"severity": "MODERATE",
"cvss_base_score": null,
"fixed_versions": [
"[trimmed-depth]"
]
}
]
},
"community": {
"stackoverflow": {
"questions_found": 5,
"top": [
{
"title": "[trimmed-depth]",
"score": "[trimmed-depth]",
"is_answered": "[trimmed-depth]",
"link": "[trimmed-depth]"
},
{
"title": "[trimmed-depth]",
"score": "[trimmed-depth]",
"is_answered": "[trimmed-depth]",
"link": "[trimmed-depth]"
},
{
"title": "[trimmed-depth]",
"score": "[trimmed-depth]",
"is_answered": "[trimmed-depth]",
"link": "[trimmed-depth]"
}
]
}
},
"score": {
"popularity": {
"score": 98,
"inputs": {
"monthly_downloads": 617129388,
"stars": 61242
},
"method": "mean of log-scaled monthly-downloads (full≈50M) and github stars (full≈100k)"
},
"maintenance": {
"score": 67,
"inputs": {
"days_since_last_release": 107,
"days_since_last_repo_push": 14,
"releases_last_12mo": 2,
"archived": false,
"contributors": 30
},
"method": "exp-decay release/push recency + release cadence + contributor bus-factor (equal-weighted over available signals)"
},
"security": {
"score": 0,
"inputs": {
"vuln_query_ok": true,
"vuln_count": 6,
"severity_summary": {
"by_severity": "[trimmed-depth]",
"highest_severity": "[trimmed-depth]",
"total": "[trimmed-depth]"
},
"version_pinned": true,
"repo_match_trusted": true,
"known_exploited_count": null
},
"note": "security score is a risk SIGNAL, not a guarantee of safety",
"method": "100 − weighted severity penalty (crit45/high25/mod10/low4) for the PINNED version; KEV known-exploited floors the score to ≤10"
},
"license": {
"score": "unknown",
"inputs": {
"license": "MIT",
"spdx_id": "NOASSERTION"
},
"tier": "unknown",
"method": "SPDX tier: permissive→90, weak-copyleft→55, strong-copyleft/SSPL→25, none/unknown→unknown",
"missing_reason": "no license declared in registry/repo metadata",
"note": "license tier reflects integration friction for an API/SaaS consumer, not legal advice"
},
"dependency_risk": {
"score": 100,
"inputs": {
"direct_dependencies": 0,
"vulnerable_dependencies": null,
"dependencies_scanned": null
},
"method": "exp-decay fan-out penalty (direct deps) + clean-dependency ratio when deps were vuln-scanned"
},
"overall": {
"trust_score": 56,
"grade": "C",
"scored_components": [
"dependency_risk",
"maintenance",
"popularity"
],
"unknown_components": [
"license"
],
"confidence": 0.85,
"method": "weighted mean of resolved sub-scores (security0.30/pop0.20/maint0.20/license0.15/dep0.15), renormalized over available components — unknowns excluded, not treated as 0 or 100"
}
}
}
}What the Package & Dependency Trust API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| package_trust | ecosystem+package → registry metadata + downloads + maintainers + license + resolved repository health + vulnerabilities + partial trust score (with per-sub-score inputs) | Platform and DevOps teams call package_trust to get ecosystem+package → registry metadata + downloads + maintainers + license + resolved reposito…. | ecosystem, package, version, mode |
| repo_trust | owner/repo → repository health + release cadence + bus-factor signal + repo-anchored trust sub-scores (popularity/maintenance), independent of any registry | Security and supply-chain teams call repo_trust to get owner/repo → repository health + release cadence + bus-factor signal + repo-anchored trust su…. | owner, repo |
| lockfile_scan | manifest/lockfile text → dependency list + each dep's vuln/risk summary via one batched vuln scan (BOUNDED: direct + lockfile-pinned deps, max 100; truncated:true when capped). package.json/lock, requirements.txt, go.sum/mod, Cargo.lock, Gemfile.lock | Developer-tool builders call lockfile_scan to get manifest/lockfile text → dependency list + each dep's vuln/risk summary via one batched vuln…. | content, filename, ecosystem |
| batch | trust-score up to 10 packages in one call (basic depth, per-item ok/error) | AI-agent developers call batch to get trust-score up to 10 packages in one call (basic depth, per-item ok/error). | items |
Call package_trust from your stack
curl -X POST https://api.reefapi.com/enrich-package/v1/package_trust \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"ecosystem":"npm","package":"lodash","version":"4.17.15","mode":"rich"}'import requests
r = requests.post(
"https://api.reefapi.com/enrich-package/v1/package_trust",
headers={"x-api-key": REEF_KEY},
json={
"ecosystem": "npm",
"package": "lodash",
"version": "4.17.15",
"mode": "rich"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/enrich-package/v1/package_trust", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"ecosystem": "npm",
"package": "lodash",
"version": "4.17.15",
"mode": "rich"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.enrich-package.package_trust with {"ecosystem":"npm","package":"lodash","version":"4.17.15","mode":"rich"}.Who uses this API and why
- Security teams call lockfile_scan to trust-score every dependency in a project at once.
- License-compliance tools use package_trust to catch copyleft or risky licenses before adoption.
- Engineering teams use repo_trust to vet a library's maintenance and health before depending on it.
Questions developers ask before integrating
metadata.license says MIT, so why is the license sub-score "unknown"?
Because the sub-score reads the SPDX id from the resolved GitHub repository, not the string the registry prints. A measured lodash call returned metadata.license.value "MIT" alongside repo.health.license "NOASSERTION", which is what GitHub reports when its licence detector cannot classify the LICENSE file. The score block then carried spdx_id "NOASSERTION", tier "unknown" and missing_reason "no license declared in registry/repo metadata". Read metadata.license.value for what the publisher declared, and read score.license.tier only when spdx_id is a real identifier.
Why did the same package and version score differently in two calls?
Because mode changes which inputs exist. lodash 4.17.15 came back with trust_score 56 under mode "rich" and 62 through the batch action, which runs at basic depth. The whole difference is maintenance: rich resolved days_since_last_release 147, releases_last_12mo 2 and contributors 30 and scored 65, while basic had all three as null and scored 90 on push recency alone. The method line says it plainly, "equal-weighted over available signals", so fewer signals can produce a higher number. Only compare packages scored at the same mode.
Does passing a version actually change the security score?
It changes the method, not just the number. With a version pinned the response scores only the advisories affecting that exact release: lodash 4.17.15 got 100 minus a weighted severity penalty and landed on 0. Without a version it scores the historical advisory set for the whole package using a severity floor and a saturating count term, and adds the flag historical_vulns_across_all_versions_pin_a_version_for_precise_read. Measured, requests unpinned scored 55 across 16 advisories. If you are auditing what you actually ship, pass the installed version.
What comes back for a package that does not exist?
An error, not an empty success. A measured call for npm package zzz-this-package-does-not-exist-9q8x7 returned ok:false with error.code NOT_FOUND, retryable false, the message "package 'zzz-this-package-does-not-exist-9q8x7' not found in npm" and meta.record_count 0. A bad ecosystem is rejected earlier and differently: ecosystem "cocoapods" returned INVALID_PARAM in under 2 ms with error.detail.allowed listing the six accepted values. Neither path substitutes a similarly named package.
Why does a Go or Maven package come back so thin?
Because full registry metadata and download counts exist only for npm and PyPI. A measured lookup on github.com/gin-gonic/gin returned meta.completeness_pct 20.0, provenance.registry status "unsupported", repo found false with reason "no_repo_field", and four of the five sub-scores as "unknown". You still get a real vulnerability scan: 8 advisories, one critical, each with fixed_versions. The overall grade was withheld as "insufficient-data" rather than dressed up as a letter.
vulnerabilities.count is 16 but items[] is empty. Is that a bug?
No, that is mode "basic". The count, highest_severity and the by_severity breakdown are always populated; the per-advisory list with ids, CVEs, summaries and fixed_versions ships only under mode "rich", which is the default on package_trust and is not available through batch. A measured rich lodash call returned 6 items including GHSA-35jh-r3h4-6jhm with cve_ids CVE-2021-23337 and CVE-2026-4800 and fixed_versions ["4.17.21"]. Note that cvss_base_score came back null on every advisory row we measured, so severity is the field to key on.
Does lockfile_scan give me severities?
Not reliably today. A measured requirements.txt scan reported total_vulns 20 with by_severity {critical:0, high:0, moderate:0, low:0, unknown:20} and highest_severity null on the summary and on every dependency row, while package_trust on one of those same packages classified its advisories as 1 high and 7 moderate. Use lockfile_scan to find which dependencies carry advisories and how many, then re-run the interesting ones through package_trust for the severity read. The scan is also bounded: direct plus lockfile-pinned dependencies, maximum 100, with manifest.truncated telling you when the cap bit.
What does repo.match.confidence mean, and when should I distrust the repo health?
It scores how the GitHub repository was tied to the package. Measured values: 1.0 with class "explicit-input" when you called repo_trust with owner and repo yourself, 0.95 with class "declared-exact" when the package's own repository field pointed at a repo whose name matched, with evidence strings spelling out the parse and a name_similarity of 1.00, and 0.0 with class "none" and reason "no_repo_field" for the Go module. A weak match also surfaces in the security flags as repo_unverified_health_half_excluded, so the score already discounts it.
What is the Package & Dependency Trust API?
Package & Dependency Trust API is a ReefAPI endpoint group for package & dependency trust It returns live JSON through POST requests under /enrich-package/v1.
Is the Package & Dependency Trust API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Package & Dependency Trust calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Package & Dependency Trust login or account?
No login to Package & Dependency Trust 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 Package & Dependency Trust data?
The page example is captured from a live package_trust call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Package & Dependency Trust API use?
Package & Dependency Trust actions currently cost 2-5 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Package & Dependency Trust from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call enrich-package actions with the same key, credit pool and JSON envelope used by normal REST requests.