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

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.2.0.tar.gz (102.3 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.2.0-py3-none-any.whl (37.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: vulners_py-1.2.0.tar.gz
  • Upload date:
  • Size: 102.3 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for vulners_py-1.2.0.tar.gz
Algorithm Hash digest
SHA256 bdf3f4a4604a7ed34db278b67e1d00562f1be6895de2f950a0966ba9961c0da4
MD5 d119cb408c3d64f91d8ccebfe71b1632
BLAKE2b-256 a36063dc38e275f7fdaa06feda6e690b234ed674303dba677d9cd177475f5760

See more details on using hashes here.

File details

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

File metadata

  • Download URL: vulners_py-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 37.8 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for vulners_py-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a4752bc286a39b700090d0102256b7c3f914696663f857ca05af489a2bc39006
MD5 234192be229a5c7693a6b8bb24723587
BLAKE2b-256 23a4de905c753122c5ff4faea099c903a9a3586b2d8c43fe64b910b754b1217c

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