Skip to main content

vulners-py

Python License PyPI

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, single-bulletin lookup, multi-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

Versioning policy

The 1.x series is the SDK's public-API stabilization period. Patch releases preserve compatibility; minor releases may include clearly documented breaking API refinements when correcting an early design before further feature development avoids long-term compatibility debt. Review CHANGELOG.md before upgrading to a new 1.x minor release.

Starting with 2.0.0, public API compatibility follows strict Semantic Versioning.

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="..."). The transport authenticates with X-Api-Key, never places the key in a URL, and discards HTTP cookies. Client and transport 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.bulletins.by_id("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.iter_exploits("CVE-2021-44228"):
            print(bulletin.id)


asyncio.run(main())

Namespaces

Namespace Capabilities
search Paginated bulletin/exploit searches, history, and web vulnerability matching
bulletins Bulletin lookup by ID, 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

Search and bulletin lookup

The bulletins namespace owns bulletin lookup by ID. The search namespace owns Lucene search; use its paged methods when you need result metadata or explicit offsets, and the iter_* methods when you want lazy, auto-paginated results:

Method Result
client.bulletins.by_id(id) One SearchDocument, or None when the ID is not found
client.bulletins.by_ids(ids) Found bulletins in the requested ID order
client.search.bulletins(query, ...) One SearchPage with documents and total metadata
client.search.iter_bulletins(query, ...) Lazy iterator or async iterator over all matches
client.search.exploits(query, ...) One exploit-filtered SearchPage
client.search.iter_exploits(query, ...) Lazy iterator or async iterator over all exploits

Reference and KB helpers also live under client.bulletins:

with Vulners() as client:
    bulletins = client.bulletins.by_ids(("CVE-2024-23622", "CVE-2021-44228"))
    references = client.bulletins.references("CVE-2024-23622")

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.iter_bulletins(query)
find_exploit(query) client.search.exploits(query)
get_bulletin(id) client.bulletins.by_id(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

Development requires uv and just. Then install all optional and development dependencies:

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.

Release files for vulners-py 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vulners-py 1.2.0
File Size Uploaded
vulners_py-1.2.0.tar.gz 102.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vulners-py 1.2.0
File Interpreter ABI Platform
vulners_py-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size:140.1 kB

Release files / vulners_py-1.2.0.tar.gz

Download URL vulners_py-1.2.0.tar.gz
Size 102.3 kB
Tags Source
SHA-256 checksum
How to use checksums
bdf3f4a4604a7ed34db278b67e1d00562f1be6895de2f950a0966ba9961c0da4
BLAKE2b-256 checksum
How to use checksums
a36063dc38e275f7fdaa06feda6e690b234ed674303dba677d9cd177475f5760
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / vulners_py-1.2.0-py3-none-any.whl

Download URL vulners_py-1.2.0-py3-none-any.whl
Size 37.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a4752bc286a39b700090d0102256b7c3f914696663f857ca05af489a2bc39006
BLAKE2b-256 checksum
How to use checksums
23a4de905c753122c5ff4faea099c903a9a3586b2d8c43fe64b910b754b1217c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page