Skip to content

Clients

The two entry points to the v4 API. Both take the same arguments and expose the same resource namespaces (search, documents, audit, archive, misc, report, stix, subscriptions, subscriptions_email, webhooks, vscanner); the only difference is that AsyncVulners methods are coroutines.

from vulners import Vulners, AsyncVulners

Vulners

vulners.Vulners

Vulners(api_key: str | SecretStr | None = None, *, base_url: str | URL | None = None, timeout: float | Timeout | None = None, max_retries: int | None = None, max_response_bytes: int | None = None, http2: bool = True, proxy: str | Proxy | None = None, verify: bool | str | SSLContext = True, trust_env: bool = True, before_request: Hook | Sequence[Hook] | None = None, after_response: Hook | Sequence[Hook] | None = None, on_error: Hook | Sequence[Hook] | None = None, http_client: Client | None = None)

Synchronous Vulners API client.

Create a synchronous Vulners API client.

Parameters:

Name Type Description Default
api_key str | SecretStr | None

Your Vulners API key, as a str or pydantic.SecretStr. If omitted or empty, falls back to the VULNERS_API_KEY environment variable; when neither is set, VulnersError is raised. Get a free key at https://vulners.com.

None
base_url str | URL | None

Override the API base URL. If omitted, falls back to the VULNERS_BASE_URL environment variable, then to https://vulners.com.

None
timeout float | Timeout | None

Per-request timeout, in seconds or as an httpx.Timeout. None (the default) uses the built-in timeout profiles (a 60s read budget for normal calls, 300s for archive/bulk streams).

None
max_retries int | None

How many times to retry a failed request (connection errors and retryable status codes, honouring Retry-After). None uses the built-in default (2).

None
max_response_bytes int | None

Optional cap on the decoded/decompressed response size, in bytes. None (the default) leaves decompression unbounded so legitimate multi-gigabyte archive downloads succeed; set it to guard against decompression-bomb amplification when pointing base_url at an untrusted host.

None
http2 bool

Negotiate HTTP/2 on the SDK-owned transport (default True; h2 is a core dependency, so no extra is needed). HTTP/2 multiplexes many concurrent API calls over one connection. Pass http2=False to force HTTP/1.1 — preferable for a huge single-stream archive download, where HTTP/1.1 avoids h2's flow-control window overhead on one long body. Ignored when you pass your own http_client (set it on that client instead).

True
proxy str | Proxy | None

Route all SDK traffic through this proxy (URL string or httpx.Proxy). Applies to the SDK-owned transport; cannot be combined with http_client=.

None
verify bool | str | SSLContext

TLS verification for the SDK-owned transport: True (default), False, a CA-bundle path, or an ssl.SSLContext. Cannot be combined with http_client=.

True
trust_env bool

Whether the SDK-owned client trusts environment settings — TLS (SSL_CERT_FILE/SSL_CERT_DIR) and proxies (HTTPS_PROXY/HTTP_PROXY/ALL_PROXY, honouring NO_PROXY) when proxy= is not given. Cannot be combined with http_client=.

True
before_request Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the httpx.Request before it is sent (an httpx request event hook on the SDK-owned client).

None
after_response Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the httpx.Response when it arrives (an httpx response event hook on the SDK-owned client).

None
on_error Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the final error when a request fails for good (after retries). Exceptions raised by a hook propagate to the caller.

None
http_client Client | None

Bring your own httpx.Client (e.g. for custom proxies, transport or connection limits). Its transport is wrapped with the SDK's credential-safety guard — scoped to the SDK's own requests — so the X-Api-Key is still stripped on cross-origin redirects while your application's own traffic through the shared client is left untouched. A client you pass is not closed by this client's close().

None
Source code in src/vulners/_client.py
def __init__(
    self,
    api_key: str | SecretStr | None = None,
    *,
    base_url: str | httpx.URL | None = None,
    timeout: float | httpx.Timeout | None = None,
    max_retries: int | None = None,
    max_response_bytes: int | None = None,
    http2: bool = True,
    proxy: str | httpx.Proxy | None = None,
    verify: bool | str | ssl.SSLContext = True,
    trust_env: bool = True,
    before_request: Hook | Sequence[Hook] | None = None,
    after_response: Hook | Sequence[Hook] | None = None,
    on_error: Hook | Sequence[Hook] | None = None,
    http_client: httpx.Client | None = None,
) -> None:
    """Create a synchronous Vulners API client.

    Args:
        api_key: Your Vulners API key, as a ``str`` or ``pydantic.SecretStr``.
            If omitted or empty, falls back to the ``VULNERS_API_KEY``
            environment variable; when neither is set, ``VulnersError`` is
            raised. Get a free key at https://vulners.com.
        base_url: Override the API base URL. If omitted, falls back to the
            ``VULNERS_BASE_URL`` environment variable, then to
            ``https://vulners.com``.
        timeout: Per-request timeout, in seconds or as an ``httpx.Timeout``.
            ``None`` (the default) uses the built-in timeout profiles (a 60s
            read budget for normal calls, 300s for archive/bulk streams).
        max_retries: How many times to retry a failed request (connection
            errors and retryable status codes, honouring ``Retry-After``).
            ``None`` uses the built-in default (2).
        max_response_bytes: Optional cap on the decoded/decompressed response
            size, in bytes. ``None`` (the default) leaves decompression
            unbounded so legitimate multi-gigabyte archive downloads succeed;
            set it to guard against decompression-bomb amplification when
            pointing ``base_url`` at an untrusted host.
        http2: Negotiate HTTP/2 on the SDK-owned transport (default ``True``;
            ``h2`` is a core dependency, so no extra is needed). HTTP/2
            multiplexes many concurrent API calls over one connection. Pass
            ``http2=False`` to force HTTP/1.1 — preferable for a huge
            single-stream archive download, where HTTP/1.1 avoids h2's
            flow-control window overhead on one long body. Ignored when you
            pass your own ``http_client`` (set it on that client instead).
        proxy: Route all SDK traffic through this proxy (URL string or
            ``httpx.Proxy``). Applies to the SDK-owned transport; cannot be
            combined with ``http_client=``.
        verify: TLS verification for the SDK-owned transport: ``True``
            (default), ``False``, a CA-bundle path, or an
            ``ssl.SSLContext``. Cannot be combined with ``http_client=``.
        trust_env: Whether the SDK-owned client trusts environment settings —
            TLS (``SSL_CERT_FILE``/``SSL_CERT_DIR``) and proxies
            (``HTTPS_PROXY``/``HTTP_PROXY``/``ALL_PROXY``, honouring
            ``NO_PROXY``) when ``proxy=`` is not given. Cannot be combined
            with ``http_client=``.
        before_request: Callable (or sequence of callables) invoked with the
            ``httpx.Request`` before it is sent (an httpx request event
            hook on the SDK-owned client).
        after_response: Callable (or sequence of callables) invoked with the
            ``httpx.Response`` when it arrives (an httpx response event
            hook on the SDK-owned client).
        on_error: Callable (or sequence of callables) invoked with the final
            error when a request fails for good (after retries). Exceptions
            raised by a hook propagate to the caller.
        http_client: Bring your own ``httpx.Client`` (e.g. for custom proxies,
            transport or connection limits). Its transport is wrapped with the
            SDK's credential-safety guard — scoped to the SDK's own requests —
            so the ``X-Api-Key`` is still stripped on cross-origin redirects
            while your application's own traffic through the shared client is
            left untouched. A client you pass is not closed by this client's
            ``close()``.
    """
    _check_byo_conflicts(
        http_client, proxy, verify, trust_env, before_request, after_response
    )
    config = resolve_config(
        api_key=api_key,
        base_url=base_url,
        version=__version__,
        timeout=timeout,
        max_retries=max_retries,
        max_response_bytes=max_response_bytes,
        http2=http2,
        proxy=proxy,
        verify=verify,
        trust_env=trust_env,
        before_request=_sync_hooks(before_request, "before_request"),
        after_response=_sync_hooks(after_response, "after_response"),
        on_error=_sync_hooks(on_error, "on_error"),
    )
    install_key_redaction(config.api_key.get_secret_value())
    self._api = SyncAPIClient(config, http_client=http_client)

config property

config: ClientConfig

The resolved, immutable client configuration.

base_url property

base_url: URL

The API base URL requests are sent to.

reports property

reports: Report

Alias of :attr:report (the primary plural name).

subscriptions cached property

subscriptions: SubscriptionsV4

The v4 subscriptions CRUD (list/get/create/update/delete).

subscriptions_email cached property

subscriptions_email: Subscriptions

The legacy v3 email subscriptions (moved here from subscriptions).

is_closed property

is_closed: bool

Whether the underlying connection pool has been closed.

get

get(path: str, *, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

GET an arbitrary API path; params become the query string.

Source code in src/vulners/_client.py
def get(
    self,
    path: str,
    *,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """GET an arbitrary API ``path``; ``params`` become the query string."""
    return self._api.get(path, params=params, timeout=timeout)

post

post(path: str, *, json: Any = None, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

POST json to an arbitrary API path; params add query args.

Source code in src/vulners/_client.py
def post(
    self,
    path: str,
    *,
    json: Any = None,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """POST ``json`` to an arbitrary API ``path``; ``params`` add query args."""
    return self._api.post(path, body=json, params=params, timeout=timeout)

put

put(path: str, *, json: Any = None, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

PUT json to an arbitrary API path; params add query args.

Source code in src/vulners/_client.py
def put(
    self,
    path: str,
    *,
    json: Any = None,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """PUT ``json`` to an arbitrary API ``path``; ``params`` add query args."""
    return self._api.put(path, body=json, params=params, timeout=timeout)

delete

delete(path: str, *, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

DELETE an arbitrary API path; params become the query string.

Source code in src/vulners/_client.py
def delete(
    self,
    path: str,
    *,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """DELETE an arbitrary API ``path``; ``params`` become the query string."""
    return self._api.delete(path, params=params, timeout=timeout)

with_options

with_options(*, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int | NotGiven = not_given, max_response_bytes: int | None | NotGiven = not_given) -> Vulners

A copy of this client sharing the same connection pool, with overrides.

Source code in src/vulners/_client.py
def with_options(
    self,
    *,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
    max_retries: int | NotGiven = not_given,
    max_response_bytes: int | None | NotGiven = not_given,
) -> Vulners:
    """A copy of this client sharing the same connection pool, with overrides."""
    changes = _option_changes(timeout, max_retries, max_response_bytes)
    clone = object.__new__(type(self))
    # The shared httpx client is injected, so the clone's own api treats the
    # pool as externally-supplied and never closes it; the parent's pacing
    # buckets are shared so rate-limit pacing is not reset.
    clone._api = SyncAPIClient(
        self._api.config.replace(**changes),
        http_client=self._api._client,
        buckets=self._api._buckets,
    )
    # Keep the pool's real owner alive for the clone's whole lifetime, and
    # route close through it. Without this, `Vulners(key).with_options(...)`
    # would drop the temporary owner immediately — its finalizer closing the
    # shared pool out from under the clone (RuntimeError on the next request).
    clone._owner = getattr(self, "_owner", self)
    return clone

close

close() -> None

Close the underlying connection pool.

A no-op for an http_client you passed in; prefer the with context manager. A with_options() clone shares one httpx client with its owner (and any sibling clones), so closing any of them closes that shared pool for all — use separate clients if you need independent lifetimes.

Source code in src/vulners/_client.py
def close(self) -> None:
    """Close the underlying connection pool.

    A no-op for an ``http_client`` you passed in; prefer the ``with`` context
    manager. A ``with_options()`` clone shares one httpx client with its owner
    (and any sibling clones), so closing any of them closes that shared pool for
    all — use separate clients if you need independent lifetimes.
    """
    owner = getattr(self, "_owner", None)
    if owner is not None:
        # Clone: close the shared pool through its owner (the clone's own _api
        # treats the pool as borrowed and would no-op). Drop the ref so a second
        # close is a safe no-op and the owner can be collected.
        owner.close()
        self._owner = None
    else:
        self._api.close()

AsyncVulners

vulners.AsyncVulners

AsyncVulners(api_key: str | SecretStr | None = None, *, base_url: str | URL | None = None, timeout: float | Timeout | None = None, max_retries: int | None = None, max_response_bytes: int | None = None, http2: bool = True, proxy: str | Proxy | None = None, verify: bool | str | SSLContext = True, trust_env: bool = True, before_request: Hook | Sequence[Hook] | None = None, after_response: Hook | Sequence[Hook] | None = None, on_error: Hook | Sequence[Hook] | None = None, http_client: AsyncClient | None = None)

Asynchronous Vulners API client.

Create an asynchronous Vulners API client.

Parameters:

Name Type Description Default
api_key str | SecretStr | None

Your Vulners API key, as a str or pydantic.SecretStr. If omitted or empty, falls back to the VULNERS_API_KEY environment variable; when neither is set, VulnersError is raised. Get a free key at https://vulners.com.

None
base_url str | URL | None

Override the API base URL. If omitted, falls back to the VULNERS_BASE_URL environment variable, then to https://vulners.com.

None
timeout float | Timeout | None

Per-request timeout, in seconds or as an httpx.Timeout. None (the default) uses the built-in timeout profiles (a 60s read budget for normal calls, 300s for archive/bulk streams).

None
max_retries int | None

How many times to retry a failed request (connection errors and retryable status codes, honouring Retry-After). None uses the built-in default (2).

None
max_response_bytes int | None

Optional cap on the decoded/decompressed response size, in bytes. None (the default) leaves decompression unbounded so legitimate multi-gigabyte archive downloads succeed; set it to guard against decompression-bomb amplification when pointing base_url at an untrusted host.

None
http2 bool

Negotiate HTTP/2 on the SDK-owned transport (default True; h2 is a core dependency, so no extra is needed). HTTP/2 multiplexes many concurrent API calls over one connection. Pass http2=False to force HTTP/1.1 — preferable for a huge single-stream archive download, where HTTP/1.1 avoids h2's flow-control window overhead on one long body. Ignored when you pass your own http_client (set it on that client instead).

True
proxy str | Proxy | None

Route all SDK traffic through this proxy (URL string or httpx.Proxy). Applies to the SDK-owned transport; cannot be combined with http_client=.

None
verify bool | str | SSLContext

TLS verification for the SDK-owned transport: True (default), False, a CA-bundle path, or an ssl.SSLContext. Cannot be combined with http_client=.

True
trust_env bool

Whether the SDK-owned client trusts environment settings — TLS (SSL_CERT_FILE/SSL_CERT_DIR) and proxies (HTTPS_PROXY/HTTP_PROXY/ALL_PROXY, honouring NO_PROXY) when proxy= is not given. Cannot be combined with http_client=.

True
before_request Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the httpx.Request before it is sent (an httpx request event hook on the SDK-owned client). Sync or async callables.

None
after_response Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the httpx.Response when it arrives (an httpx response event hook on the SDK-owned client). Sync or async callables.

None
on_error Hook | Sequence[Hook] | None

Callable (or sequence of callables) invoked with the final error when a request fails for good (after retries). Sync or async callables; exceptions raised by a hook propagate.

None
http_client AsyncClient | None

Bring your own httpx.AsyncClient (e.g. for custom proxies, transport or connection limits). Its transport is wrapped with the SDK's credential-safety guard — scoped to the SDK's own requests — so the X-Api-Key is still stripped on cross-origin redirects while your application's own traffic through the shared client is left untouched. A client you pass is not closed by this client's aclose().

None
Source code in src/vulners/_client.py
def __init__(
    self,
    api_key: str | SecretStr | None = None,
    *,
    base_url: str | httpx.URL | None = None,
    timeout: float | httpx.Timeout | None = None,
    max_retries: int | None = None,
    max_response_bytes: int | None = None,
    http2: bool = True,
    proxy: str | httpx.Proxy | None = None,
    verify: bool | str | ssl.SSLContext = True,
    trust_env: bool = True,
    before_request: Hook | Sequence[Hook] | None = None,
    after_response: Hook | Sequence[Hook] | None = None,
    on_error: Hook | Sequence[Hook] | None = None,
    http_client: httpx.AsyncClient | None = None,
) -> None:
    """Create an asynchronous Vulners API client.

    Args:
        api_key: Your Vulners API key, as a ``str`` or ``pydantic.SecretStr``.
            If omitted or empty, falls back to the ``VULNERS_API_KEY``
            environment variable; when neither is set, ``VulnersError`` is
            raised. Get a free key at https://vulners.com.
        base_url: Override the API base URL. If omitted, falls back to the
            ``VULNERS_BASE_URL`` environment variable, then to
            ``https://vulners.com``.
        timeout: Per-request timeout, in seconds or as an ``httpx.Timeout``.
            ``None`` (the default) uses the built-in timeout profiles (a 60s
            read budget for normal calls, 300s for archive/bulk streams).
        max_retries: How many times to retry a failed request (connection
            errors and retryable status codes, honouring ``Retry-After``).
            ``None`` uses the built-in default (2).
        max_response_bytes: Optional cap on the decoded/decompressed response
            size, in bytes. ``None`` (the default) leaves decompression
            unbounded so legitimate multi-gigabyte archive downloads succeed;
            set it to guard against decompression-bomb amplification when
            pointing ``base_url`` at an untrusted host.
        http2: Negotiate HTTP/2 on the SDK-owned transport (default ``True``;
            ``h2`` is a core dependency, so no extra is needed). HTTP/2
            multiplexes many concurrent API calls over one connection. Pass
            ``http2=False`` to force HTTP/1.1 — preferable for a huge
            single-stream archive download, where HTTP/1.1 avoids h2's
            flow-control window overhead on one long body. Ignored when you
            pass your own ``http_client`` (set it on that client instead).
        proxy: Route all SDK traffic through this proxy (URL string or
            ``httpx.Proxy``). Applies to the SDK-owned transport; cannot be
            combined with ``http_client=``.
        verify: TLS verification for the SDK-owned transport: ``True``
            (default), ``False``, a CA-bundle path, or an
            ``ssl.SSLContext``. Cannot be combined with ``http_client=``.
        trust_env: Whether the SDK-owned client trusts environment settings —
            TLS (``SSL_CERT_FILE``/``SSL_CERT_DIR``) and proxies
            (``HTTPS_PROXY``/``HTTP_PROXY``/``ALL_PROXY``, honouring
            ``NO_PROXY``) when ``proxy=`` is not given. Cannot be combined
            with ``http_client=``.
        before_request: Callable (or sequence of callables) invoked with the
            ``httpx.Request`` before it is sent (an httpx request event
            hook on the SDK-owned client). Sync or async callables.
        after_response: Callable (or sequence of callables) invoked with the
            ``httpx.Response`` when it arrives (an httpx response event
            hook on the SDK-owned client). Sync or async callables.
        on_error: Callable (or sequence of callables) invoked with the final
            error when a request fails for good (after retries). Sync or
            async callables; exceptions raised by a hook propagate.
        http_client: Bring your own ``httpx.AsyncClient`` (e.g. for custom
            proxies, transport or connection limits). Its transport is wrapped
            with the SDK's credential-safety guard — scoped to the SDK's own
            requests — so the ``X-Api-Key`` is still stripped on cross-origin
            redirects while your application's own traffic through the shared
            client is left untouched. A client you pass is not closed by this
            client's ``aclose()``.
    """
    _check_byo_conflicts(
        http_client, proxy, verify, trust_env, before_request, after_response
    )
    config = resolve_config(
        api_key=api_key,
        base_url=base_url,
        version=__version__,
        timeout=timeout,
        max_retries=max_retries,
        max_response_bytes=max_response_bytes,
        http2=http2,
        proxy=proxy,
        verify=verify,
        trust_env=trust_env,
        before_request=_async_hooks(before_request),
        after_response=_async_hooks(after_response),
        on_error=_async_hooks(on_error),
    )
    install_key_redaction(config.api_key.get_secret_value())
    self._api = AsyncAPIClient(config, http_client=http_client)

config property

config: ClientConfig

The resolved, immutable client configuration.

base_url property

base_url: URL

The API base URL requests are sent to.

reports property

reports: AsyncReport

Alias of :attr:report (the primary plural name).

subscriptions cached property

subscriptions: AsyncSubscriptionsV4

The v4 subscriptions CRUD (list/get/create/update/delete).

subscriptions_email cached property

subscriptions_email: AsyncSubscriptions

The legacy v3 email subscriptions (moved here from subscriptions).

is_closed property

is_closed: bool

Whether the underlying connection pool has been closed.

get async

get(path: str, *, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

GET an arbitrary API path; params become the query string.

Source code in src/vulners/_client.py
async def get(
    self,
    path: str,
    *,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """GET an arbitrary API ``path``; ``params`` become the query string."""
    return await self._api.get(path, params=params, timeout=timeout)

post async

post(path: str, *, json: Any = None, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

POST json to an arbitrary API path; params add query args.

Source code in src/vulners/_client.py
async def post(
    self,
    path: str,
    *,
    json: Any = None,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """POST ``json`` to an arbitrary API ``path``; ``params`` add query args."""
    return await self._api.post(path, body=json, params=params, timeout=timeout)

put async

put(path: str, *, json: Any = None, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

PUT json to an arbitrary API path; params add query args.

Source code in src/vulners/_client.py
async def put(
    self,
    path: str,
    *,
    json: Any = None,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """PUT ``json`` to an arbitrary API ``path``; ``params`` add query args."""
    return await self._api.put(path, body=json, params=params, timeout=timeout)

delete async

delete(path: str, *, params: Mapping[str, Any] | None = None, timeout: float | Timeout | None | NotGiven = not_given) -> Any

DELETE an arbitrary API path; params become the query string.

Source code in src/vulners/_client.py
async def delete(
    self,
    path: str,
    *,
    params: Mapping[str, Any] | None = None,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Any:
    """DELETE an arbitrary API ``path``; ``params`` become the query string."""
    return await self._api.delete(path, params=params, timeout=timeout)

with_options

with_options(*, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int | NotGiven = not_given, max_response_bytes: int | None | NotGiven = not_given) -> AsyncVulners

A copy of this client sharing the same connection pool, with overrides.

Source code in src/vulners/_client.py
def with_options(
    self,
    *,
    timeout: float | httpx.Timeout | None | NotGiven = not_given,
    max_retries: int | NotGiven = not_given,
    max_response_bytes: int | None | NotGiven = not_given,
) -> AsyncVulners:
    """A copy of this client sharing the same connection pool, with overrides."""
    changes = _option_changes(timeout, max_retries, max_response_bytes)
    clone = object.__new__(type(self))
    # The shared httpx client is injected, so the clone's own api treats the
    # pool as externally-supplied and never closes it; the parent's pacing
    # buckets are shared so rate-limit pacing is not reset.
    clone._api = AsyncAPIClient(
        self._api.config.replace(**changes),
        http_client=self._api._client,
        buckets=self._api._buckets,
    )
    # Keep the pool's real owner alive for the clone's lifetime and route
    # aclose through it. Without this the temporary owner of
    # `AsyncVulners(key).with_options(...)` would be dropped with no async
    # finalizer, leaking the pool (nothing ever closes it).
    clone._owner = getattr(self, "_owner", self)
    return clone

aclose async

aclose() -> None

Close the underlying connection pool.

A no-op for an http_client you passed in; prefer the async with context manager. A with_options() clone shares one httpx client with its owner (and any sibling clones), so closing any of them closes that shared pool for all — use separate clients if you need independent lifetimes.

Source code in src/vulners/_client.py
async def aclose(self) -> None:
    """Close the underlying connection pool.

    A no-op for an ``http_client`` you passed in; prefer the ``async with``
    context manager. A ``with_options()`` clone shares one httpx client with its
    owner (and any sibling clones), so closing any of them closes that shared
    pool for all — use separate clients if you need independent lifetimes.
    """
    owner = getattr(self, "_owner", None)
    if owner is not None:
        await owner.aclose()
        self._owner = None
    else:
        await self._api.aclose()