The new Vulners Python SDK: what's in it, and the two old tools we rebuilt with it

The Vulners Python SDK got a full rebuild — typed clients, async throughout, a real docstring on every method, and every response shape written down. Two of our own tools were the first things we put on it.
getsploit 3.0 shipped last week running on the new SDK: async search, Rich terminal output, and a local SQLite FTS5 index so it works with the network off. nmap-vulners 2.0 landed alongside it — three scripts merged into one, a new web sweep, and a proper test rig behind it.
Both had been quiet for years, and both took days rather than months. Not because we finally found the time, but because the SDK now writes down what every call takes and what comes back, so pointing an agentic coding harness at it actually works. Turns out "write good docs" and "make it agent-ready" are the same job.
Those docs are sitting there for you too. Here's what's in them.
pip install -U vulnersEvery signature, written down
api.md in the repo is a generated index of the entire public surface — every method, its full signature, its return type, and the HTTP route it hits. Generated from the source, so it can't quietly drift from reality.
On top of that, every public client, resource and model method now carries a real docstring: arguments, returns, what it raises. Shows up in your editor and on the docs site. No more guessing whether it's os= or os_name=. (It's os_name= on linux_audit, and that one has cost people an afternoon.)
Response shapes, from live data
The data model reference is the page to bookmark. Every bulletin family and every collection, field by field, with types and real examples — regenerated against the live API rather than written from memory.
The models are layered: Bulletin has the fields every document carries, family models add what a bulletinFamily shares, collection models add the source-specific bits. A field sits at the level where it's always present, which answers the question you used to figure out by experiment: can I rely on this being here?
And everything keeps extra="allow" — if we ship a field before the SDK models it, it still lands on your object instead of getting dropped.
audit/smart, which is the fun one
This one deserves its own section. Your asset inventory is full of display names; every vulnerability API wants a canonical CPE. Bridging that has always been the miserable part.
Don't bother. Throw the raw strings at it:
from vulners import Vulners
inventory = [
"Google Chrome 149.0.7827.102",
"7-Zip 24.09",
"Adobe Acrobat PDF Extension (Chrome) 26.5.2.2",
]
with Vulners() as v:
results = v.audit.smart(inventory, fields=["metrics", "exploitation", "epss"])
for item in results:
print(f"{item['input']} -> {item.get('cpe', 'no CPE match')} "
f"(confidence {item['confidence']:.2f}, fix: {item.get('fixedVersion', '-')})")
for vuln in item["vulnerabilities"]:
exploitation = vuln.get("exploitation", {})
if not exploitation.get("wildExploited"):
continue # only what's actually exploited
cvss = vuln.get("metrics", {}).get("cvss", {})
epss = next((e["epss"] for e in vuln.get("epss", []) if isinstance(e, dict)), "-")
sources = ",".join(s["type"] for s in exploitation.get("wildExploitedSources", []))
print(f" KEV {vuln['id']} cvss {cvss.get('score')} {cvss.get('severity')} "
f"epss {epss} [{sources}]")Google Chrome 149.0.7827.102 -> cpe:2.3:a:google:chrome:149.0.7827.102 (confidence 0.93, fix: 151.0.7922.174)
KEV CVE-2026-11645 cvss 8.8 HIGH epss 0.0219 [cisa,cisa_kev,vulncheck_kev]
7-Zip 24.09 -> cpe:2.3:a:7-zip:7-zip:24.09 (confidence 0.93, fix: 26.02)
KEV CVE-2025-11001 cvss 7.8 HIGH epss 0.26999 [vulncheck_kev]
Adobe Acrobat PDF Extension (Chrome) 26.5.2.2 -> cpe:2.3:a:adobe:acrobat_pdf_extension:26.5.2.2 (confidence 0.93, fix: -)
KEV CVE-2026-48294 cvss 7.4 HIGH epss 0.01906 [vulncheck_kev]Look at what it worked out. Adobe Acrobat PDF Extension (Chrome) — vendor, product, a parenthetical host browser, four-part version — lands on cpe:2.3:a:adobe:acrobat_pdf_extension:26.5.2.2. Chrome gets caught one build short of the fix. You wrote zero matching logic.
Each result comes back with a confidence score so you can decide what to trust, plus PURLs and the fixed version. Two things to know: it's billed per submitted string, so don't loop your whole estate through it without thinking, and it's still a preview endpoint, so the shape may move.
The rest of the audit family
kb_audit moved to /api/v4/audit/kb and now groups by update instead of by CVE — one finding, the KB that fixes it, and the updates it supersedes. On a box missing one cumulative update that's a single actionable ticket rather than 1,572 loose CVEs.
smart, software, host, linux_audit, library_audit and sbom_audit all take enrichment options now, and tell you which ones the server actually applied. Worth knowing: enrichment is opt-in. Without fields= or cvelist_metrics=True you get identifiers and matching evidence — no severity, no exploitation status. metrics is a per-advisory rollup, cvelistMetrics is per-CVE and about 35× the payload.
Things you no longer have to think about
HTTP/2 by default. brotli/zstd/ISA-L compression. Archive downloads over parallel range connections, and archive.iter_collection("cve") streams instead of buffering gigabytes. A real exception hierarchy — including for the legacy endpoints that hide an error inside an HTTP 200, which now raise instead of handing you junk as data. Your API key gets redacted from errors and reprs and stripped on cross-origin redirects.
If you're pointing an agent at it
Same docs, different door. AGENTS.md says which client to prefer and which calls cost credits, llms.txt indexes the docs one line each, api.md hands over every signature and route.
Or skip the code entirely and use the MCP server:
pip install "vulners[mcp]"
VULNERS_API_KEY=... vulners-mcpEight tools — search, exploits, bulletin lookup, CVE lookup, software audit, Linux audit, smart audit, package metadata. There's a managed one at mcp.vulners.com if you'd rather install nothing. Client configs are in the connect guide.
Nothing you built already breaks
3.x is a drop-in. VulnersApi, VScannerApi, every method and import path preserved, pinned by a compatibility suite. Already on the v4 client? kb_audit is the one break — os_name= instead of os=, and kb_audit_v3() gives you the old flat shape while you port.
Grab it
pip install -U vulnersPython 3.10 through 3.14, MIT, no build step. Free API key takes a minute at vulners.com, and the free tier is a real one.
- The repo — runnable samples for every task, v4 and legacy side by side
- Docs — quickstart, how-tos, full reference
- Data model reference — every family and collection, field by field
If you build something with it, tell us. If your agent got stuck somewhere, definitely tell us — that's usually a docs bug on our end.