Skip to main content

A modern, strictly typed Python SDK for the Vulners API.

Project description

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, 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

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.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

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.

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

vulners_py-1.0.1.tar.gz (100.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vulners_py-1.0.1-py3-none-any.whl (37.2 kB view details)

Uploaded Python 3

File details

Details for the file vulners_py-1.0.1.tar.gz.

File metadata

  • Download URL: vulners_py-1.0.1.tar.gz
  • Upload date:
  • Size: 100.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for vulners_py-1.0.1.tar.gz
Algorithm Hash digest
SHA256 cf1e8e0369e8f0b395a2dd62bea5a553e7c25040840b89be4e6fdcdd4d2384f9
MD5 f9b6fc455f63715804ad68a07e8403a5
BLAKE2b-256 d4ba5aed29fb63638b949389ef1fda7d40f17cd7d04590e8510b213cfc2b11a6

See more details on using hashes here.

File details

Details for the file vulners_py-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: vulners_py-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 37.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for vulners_py-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f719e59e393ce1f3cd29e7592785d6055b4e9deb4991d8c2ebb7a85380b598ea
MD5 35169e14f2a0c2f5bf8b75cbb8f1154b
BLAKE2b-256 504e9e6b11d153b23266347c668373ae86b954824a01d31f2ebb03224ac81c6c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page