L logiover
data · Jul 9, 2026 · 8 min read

How to Build a CVE Feed for AI Agents (NVD + OSV, 2026)

Merge NVD, GitHub Advisories and OSV into one deduplicated CVE feed — CVSS, CWE, affected versions, patch info per row. Keyless, for DevSecOps and AI agents.

Every vulnerability database has the same problem from the outside: NVD is authoritative but slow to enrich, GitHub Security Advisories are fast on package ranges but GitHub-shaped, and OSV covers ecosystem databases the other two miss. Query all three and you get three different schemas, three sets of rate limits, and the same CVE showing up three times. If you’re trying to feed a DevSecOps pipeline, an SBOM scanner, or — increasingly — an AI agent that answers “is this version vulnerable?”, you don’t want three feeds. You want one normalized, deduplicated stream. This guide covers how to merge those sources into a single clean feed, keyless, and how to ground an AI agent on current CVE data instead of stale training knowledge.

What’s worth extracting

Each row is one vulnerability advisory, merged and deduplicated across sources, with 19 fields. The ones that carry the analysis:

  • IdentifierscveId (NVD), ghsaId (GitHub), osvId (OSV alias). Each is nullable if a given source didn’t assign one; rows are keyed by CVE ID, or GHSA/OSV when no CVE exists.
  • Source provenancesource (which source’s fields won) and sourceFeeds, the union of every carrier the vuln appeared in (nvd, githubAdvisory, osv), plus duplicateCount for how many sources reported it before merging.
  • Descriptionsummary (short, GHSA-style) and description (the full CVE text).
  • SeveritycvssScore, cvssSeverity (LOW / MEDIUM / HIGH / CRITICAL) and cvssVector (a CVSS v3.1 string, falling back to v2).
  • Classificationweaknesses, an array of CWE IDs like CWE-79 (XSS) or CWE-89 (SQL injection).
  • Affected packagesaffected, a list of ecosystem/package entries with version ranges (introduced/fixed), and patchedVersion, the fixed version pulled from the earliest fix event.
  • References & datesreferences (advisory, patch, vendor and NVD URLs), publishedDate, lastModified and scrapedAt (all ISO 8601).

For daily triage you rank on cvssScore and cvssSeverity. For dependency work you read affected and patchedVersion. For AI grounding you embed summary + description + references so answers cite the exact CVE.

How the source actually works

The actor calls each source’s public, keyless JSON API over direct HTTP — no headless browser, no HTML parsing, no GraphQL token. The three endpoints are services.nvd.nist.gov, api.github.com/advisories and api.osv.dev. Responses are normalized into one schema, then deduplicated: the same vuln in all three collapses into a single row with sourceFeeds listing every carrier, and the richest fields win — summary from GHSA, CVSS from NVD, patch version from GHSA or OSV.

There are four modes. For a daily monitor, recent with daysBack: 1 gives you today’s CVEs:

{
  "mode": "recent",
  "daysBack": 1,
  "sources": ["nvd", "githubAdvisory"],
  "maxResults": 500,
  "useApifyProxy": true
}

For weekly triage of High-and-above advisories, add a severity floor:

{
  "mode": "recent",
  "daysBack": 7,
  "minCvss": 7,
  "sources": ["nvd", "githubAdvisory", "osv"],
  "maxResults": 1000
}

For SBOM / dependency scanning, product mode queries OSV by package:

{
  "mode": "product",
  "products": ["npm:express", "pypi:django", "maven:org.springframework:spring-core"],
  "sources": ["osv"]
}

And search mode runs an NVD keyword query (bulk mode runs many keywords at once):

{
  "mode": "search",
  "query": "log4j",
  "maxResults": 200
}

The minCvss filter keeps only advisories at or above a threshold — 7 for High-and-above, 9 for Critical — and a null score is treated as below the threshold, so minCvss: 7 drops unscored entries.

Run the CVE Security Advisory Monitor on Apify — one run merges NVD, GitHub Advisory and OSV into a deduplicated feed with CVSS, CWE, affected versions and patch info per row. Keyless, no browser, no login. Export to JSON, CSV, Excel or XML.

The access reality

All three sources have keyless tiers, and this actor uses them — but keyless means tight rate limits, so pacing and IP rotation are the whole game:

  • NVD keyless is about 5 requests per 30 seconds. GitHub’s advisories endpoint is roughly 60 requests per hour per IP. Neither is generous.
  • Proxy IP rotation spreads the limits. The actor routes through Apify datacenter proxy (useApifyProxy: true) and rotates the source IP per request to widen the effective budget; 429/5xx responses trigger exponential-backoff retry.
  • Large windows trade speed for reliability. A big daysBack or maxResults (capped at 5000 per query/window) means more pages, which means more pacing. The actor slows itself to stay under the limits rather than getting throttled.
  • Source choice affects coverage and freshness. NVD is the authority but lags on enrichment (package ranges, fix versions). GitHub Advisory adds those fast. OSV merges ecosystem DBs — RUSTSEC, PyPA, Go, npm, PyPI — that NVD covers poorly. For the widest net, query all three and let dedup keep it clean.
  • Freshness has a small lag. NVD and GitHub publish in near-real-time; daysBack: 1 returns the last 24 hours, with minutes-to-hours between disclosure and NVD enrichment.

The naive approach — scraping NVD’s HTML pages — is both slower and more fragile than calling the official JSON API, and it still leaves you to reconcile three schemas by hand. Going straight to the APIs and merging is the difference between a maintainable feed and a scraping treadmill.

Example output

One representative record (trimmed) from a recent run:

{
  "query": "last 1d",
  "source": "nvd",
  "sourceFeeds": ["nvd", "githubAdvisory"],
  "cveId": "CVE-2026-12345",
  "ghsaId": "GHSA-abcd-efgh-1234",
  "osvId": null,
  "summary": "SQL injection in package X via parameter Y",
  "publishedDate": "2026-07-02T13:00:00.000Z",
  "cvssScore": 9.8,
  "cvssSeverity": "CRITICAL",
  "cvssVector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
  "weaknesses": ["CWE-89"],
  "affected": [
    { "ecosystem": "pypi", "package": "django", "ranges": [{ "introduced": "0", "fixed": "4.2.1" }] }
  ],
  "patchedVersion": "4.2.1",
  "references": [
    "https://nvd.nist.gov/vuln/detail/CVE-2026-12345"
  ],
  "duplicateCount": 2,
  "scrapedAt": "2026-07-06T12:00:00.000Z"
}

Use cases

Who consumes a merged CVE feed and how:

  • Daily CVE triage — schedule recent with daysBack: 1 and minCvss: 7 to surface yesterday’s High-and-above CVEs each morning, ranked by severity.
  • Dependency & SBOM monitoring — feed your lockfile packages into product mode against OSV, then fail a build or open a ticket when an unpatched vuln appears; patchedVersion gives the upgrade target.
  • Patch prioritization — rank open advisories by cvssScore, affected-package criticality and patch availability, and draft remediation tickets from the top of the list.
  • Vendor / product monitoring — run search: "apache struts" or search: "wordpress plugin" weekly to watch a vendor’s or ecosystem’s new CVEs.
  • Threat-intel & SIEM feed — push merged, deduplicated advisories into a SIEM or ticketing system; the cross-source dedup prevents the same vuln firing three alerts.
  • AI-agent / RAG grounding — embed the daily feed into a vector store so a security agent answers “is package X at version N vulnerable to Y?” with a citation to the exact CVE, instead of hallucinating from stale training data. Wrapped as an MCP tool, an agent can call it with a keyword, package or date window and get structured advisories back.

Build-it-yourself vs. managed actor

Three official APIs, three keyless tiers — you can absolutely call them yourself. What you then maintain:

  • The normalization layer. Three schemas map onto one only if you keep reconciling them as each source evolves its response shape. This is the bulk of the work.
  • The dedup logic. Keying by CVE (falling back to GHSA/OSV), unioning sourceFeeds, counting duplicates, and picking the richest field per source — all edge cases you’ll hit in production.
  • Rate-limit engineering. Rotating IPs to stretch NVD’s 5-per-30s and GitHub’s 60-per-hour, plus backoff on 429/5xx, is fiddly and easy to get subtly wrong (and getting it wrong means silent gaps in your feed).
  • Scheduling and delivery. Turning it into a recurring job that lands in your warehouse, SIEM or vector store.

The managed actor is pay-per-result — you pay per saved advisory, and runs that yield zero advisories are free, which matters for a daily monitor that some days finds nothing new above your threshold. If you enjoy owning the reconciliation, build it. If you just want the merged feed to show up every morning, the managed path skips the schema-juggling and rate-limit plumbing.

Common pitfalls

Specific gotchas with cross-source CVE data:

  • Null CVSS is not “safe.” Fresh OSV entries often predate NVD enrichment and have no score yet. minCvss: 0 keeps them; minCvss: 7 drops them — so a strict filter can hide brand-new criticals that simply aren’t scored. Decide whether “unscored” means “ignore” for your use case.
  • duplicateCount is not a severity signal. A vuln appearing in all three sources means it’s well-covered, not that it’s worse. Rank on cvssScore, not carrier count.
  • Affected ranges need post-processing for exact-version checks. affected gives introduced/fixed ranges; to answer “is version N vulnerable?” you compare N against the range yourself downstream. The feed gives you the range, not the verdict.
  • CVSS version varies. cvssVector is v3.1 where available and falls back to v2. If you parse the vector, branch on the CVSS: prefix rather than assuming v3.1.
  • Source gaps are real. NVD lags enrichment; GitHub is package-and-code focused; OSV skews to open-source ecosystems. Querying a single source will systematically miss whole categories — which is the entire reason to merge all three.
  • Rate limits mean large windows are slow, not free. A 90-day recent sweep is many pages under a tight keyless budget. Expect it to take time; that’s the actor pacing itself to avoid throttling, not a hang.

Wrapping up

The value of a CVE feed isn’t any single source — it’s the merge. One normalized, deduplicated stream with CVSS, CWE, affected versions and patch info is what a triage dashboard, an SBOM gate, or an AI security agent can actually consume. If you want to own the reconciliation and rate-limit code, the APIs are all public. If you’d rather the merged feed just appear every morning and plug into your pipeline or vector store, a managed actor gets you there without the schema and throttling work.

Open the CVE Security Advisory Monitor on Apify — queries NVD, GitHub Advisory and OSV, merges and deduplicates them, and returns CVSS, CWE, affected versions, patch info and references per advisory. Keyless, no browser, no login, MCP-ready. Export to JSON, CSV, Excel or XML. Start on Apify’s free monthly credit.

Related guides