A modern, strictly typed Python SDK for the Vulners API.
Project description
vulners-py
A modern, strictly typed Python SDK for the Vulners API. It provides matching synchronous and asynchronous clients, immutable Pydantic v2 models, resilient HTTP handling, and no deprecated top-level compatibility aliases.
Features
- Search, document retrieval, software/host/package/SBOM audits, and Smart Audit.
- ZIP, gzip, JSON, and NDJSON archive decoding with a stream-to-disk option.
- Reports, v4 subscriptions, legacy email/polling subscriptions, STIX, CPE, and search helpers.
- API-key loading from
VULNERS_API_KEY, including.env-based development workflows. - Retry/backoff,
Retry-After, per-endpoint rate limiting, HTTP/2 support, and typed exceptions. - Strict mypy, Ruff, and pytest checks with sync/async contract tests.
VScanner is intentionally excluded because it is deprecated.
Requirements
- Python 3.10 or newer
- A Vulners API key
Installation
uv add vulners-py
or:
pip install vulners-py
Optional performance extras:
uv add "vulners-py[http2,orjson]"
Authentication
Create a local .env file that is not committed:
VULNERS_API_KEY=your-api-key
Load it into the environment before running an application:
set -a
source .env
set +a
The key can also be passed explicitly as Vulners(api_key="..."). Client representations never
contain the key.
Before wiring the SDK into your app, confirm the key in your .env is accepted with the bundled
preflight (one cheap, read-only call; exits 0 on success, 2 when no key is set):
uv run python examples/check_connection.py # synchronous
uv run python examples/async_check_connection.py # asynchronous
See examples/ for runnable snippets that load VULNERS_API_KEY from .env.
Quick start
Synchronous
from vulners import Vulners
with Vulners() as client:
page = client.search.bulletins("wordpress 4.7", limit=10)
document = client.documents.get("CVE-2024-23622")
for bulletin in page.documents:
print(bulletin.id, bulletin.title)
print(document)
Asynchronous
import asyncio
from vulners import AsyncVulners
async def main() -> None:
async with AsyncVulners() as client:
async for bulletin in client.search.exploits_iter("CVE-2021-44228"):
print(bulletin.id)
asyncio.run(main())
Namespaces
| Namespace | Capabilities |
|---|---|
search |
Bulletins, exploits, iterators, history, and web vulnerability matching |
documents |
Get one/many documents, references, KB seeds, and KB updates |
audit |
Software, host, Linux, library, classic OS, Windows, CVE, SBOM, and Smart Audit |
archive |
v3/v4 collections, incremental updates, distributives, and Getsploit downloads |
reports |
Vulnerability, IP, scan, and host reports |
subscriptions |
v4 lifecycle plus legacy email subscriptions under .email |
webhooks |
Legacy polling/webhook subscriptions |
stix |
STIX bundle generation by bulletin ID |
misc |
Suggestions, autocomplete, CPE lookup, and WAF rules |
Audit examples
from pathlib import Path
from vulners import Vulners
from vulners.types import AuditSoftware
with Vulners() as client:
matches = client.audit.software(
(AuditSoftware(product="curl", vendor="haxx", version="8.0"),)
)
packages = client.audit.library(("pkg:pypi/requests@2.20.0",))
sbom = client.audit.sbom(Path("bom.json"))
Smart Audit is a preview endpoint billed per
submitted software string. Calling client.audit.smart(...) may incur account charges.
Archive examples
from pathlib import Path
from vulners import Vulners
with Vulners() as client:
records = client.archive.collection_update("exploitdb", "2026-07-17T00:00:00")
client.archive.collection_v4(
"exploitdb",
raw=True,
destination=Path("exploitdb.ndjson.gz"),
)
Decoded archive calls return immutable ArchiveRecord objects. raw=True requires a destination
and streams the response without loading the archive into memory.
Subscriptions
from vulners import Vulners
from vulners.types import LuceneSubscriptionQuery, WebhookSubscriptionDelivery
query = LuceneSubscriptionQuery(query="cvss:[9 TO *] AND family:cve")
delivery = WebhookSubscriptionDelivery(
address="https://example.com/vulners",
crontab="0 * * * *",
)
with Vulners() as client:
created = client.subscriptions.create("Critical CVEs", query, delivery)
print(created.id)
Create, update, and delete calls mutate remote account state. Legacy email subscriptions are under
client.subscriptions.email; legacy polling subscriptions are under client.webhooks.
Error handling
from vulners import AuthenticationError, RateLimitError, Vulners, VulnersAPIError
try:
with Vulners() as client:
client.search.bulletins("wordpress")
except AuthenticationError:
print("Check VULNERS_API_KEY")
except RateLimitError as error:
print(f"Retry after {error.retry_after!r} seconds")
except VulnersAPIError as error:
print(f"Vulners API error {error.status_code}: {error.message}")
Migration from the legacy wrapper
| Legacy wrapper | vulners-py |
|---|---|
find(query) / search_bulletins(query) |
client.search.bulletins(query) |
find_all(query) |
client.search.bulletins_iter(query) |
find_exploit(query) |
client.search.exploits(query) |
get_bulletin(id) |
client.documents.get(id) |
audit_software(...) |
client.audit.software(...) |
winaudit(...) |
client.audit.winaudit(...) |
vulnssummary_report(...) |
client.reports.vulns_summary(...) |
No deprecated top-level aliases or DeprecationWarning shims are included.
Development
uv sync --all-extras
just check
Available recipes:
just fmt
just lint
just typecheck
just test
just check
Tests use mocked HTTP contracts by default. Run the bounded, read-only integration suite with a key
loaded from .env:
set -a
source .env
set +a
VULNERS_LIVE=1 uv run pytest tests/test_integration.py
The integration suite deliberately excludes billed Smart Audit/SBOM requests, archive bulk downloads, and subscription mutations.
License
Distributed under the MIT License. Copyright © 2026 Aleksandr Pavlov ckidoz@gmail.com.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vulners_py-1.0.0.tar.gz.
File metadata
- Download URL: vulners_py-1.0.0.tar.gz
- Upload date:
- Size: 97.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44e0b06753de0a94a268434a3351dbc4fcbf751f6be47c7769f197deff57c467
|
|
| MD5 |
101220dfdafc7892ba5a583528921074
|
|
| BLAKE2b-256 |
d8105163b444e66356512105c9c0481520b96dbe1274e857f5464a4572f96e62
|
File details
Details for the file vulners_py-1.0.0-py3-none-any.whl.
File metadata
- Download URL: vulners_py-1.0.0-py3-none-any.whl
- Upload date:
- Size: 36.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98abc82efb45ca2d5065033cde9620b1fc52b72cf260b76f16b13dc37da60568
|
|
| MD5 |
410b5edc25ec2f6fe2fdc7f991bf6b07
|
|
| BLAKE2b-256 |
05f2e1b17d5687983dbbd1ca57a9b3428c7b207cef15b59a8c753a12f27a16f8
|