Skip to content

Models & pagination

Bulletins

Search and lookup methods return Bulletin objects. The most specific model is chosen for each document: a known collection type selects a per-collection model, otherwise the bulletinFamily selects a family model, falling back to GenericBulletin. Per-collection models subclass their family model, which subclasses Bulletin, so family classes are a stable isinstance/annotation surface. Fields are accessed as attributes and are all optional (a missing field is None).

The family and per-collection models follow a base → family → type hierarchy — see Data models for every family and collection with its fields, descriptions and examples.

vulners._models.bulletin.Bulletin

Bases: VulnersModel

A single Vulners document; base for every bulletinFamily. Carries the fields present in every document. Family/type subclasses add their own; extra="allow" keeps any unmodelled field accessible.

vulners._models.bulletin.GenericBulletin

Bases: Bulletin

Fallback for any bulletinFamily without a dedicated model (forward-compat).

CVSS & nested objects

Value objects shared across families. Cvss specializes to Cvss2/Cvss3/Cvss4 by its version.

vulners._models.bulletin.Cvss

Bases: VulnersModel

CVSS score block; the base/fallback across scoring versions.

vulners._models.bulletin.Timestamps

Bases: VulnersModel

Lifecycle timestamps Vulners maintains for a document (ISO-8601 strings).

vulners._models.bulletin.Enchantments

Bases: VulnersModel

Vulners-computed enrichment layer over the raw document.

vulners._models.bulletin.EpssScore

Bases: VulnersModel

One EPSS (Exploit Prediction Scoring System) datapoint.

Pagination

search.query returns a SearchPage (async: AsyncSearchPage). It knows its place in the result window and walks further pages when you iterate it.

vulners._pagination.SearchPage dataclass

SearchPage(data: list[T] = list(), total: int | None = None, offset: int = 0, limit: int = 0, fetch: SyncFetch[T] | None = None)

Bases: Generic[T]

One page of search results, aware of its place in the result window.

has_next_page

has_next_page() -> bool

True when a further page exists and stays within the 10k window.

Source code in src/vulners/_pagination.py
def has_next_page(self) -> bool:
    """True when a further page exists and stays within the 10k window."""
    if self.fetch is None or _window_blocked(self.offset, self.limit):
        return False
    return not _short_page(len(self.data), self.limit, self.offset, self.total)

next_page

next_page() -> SearchPage[T]

Fetch the next page.

Raises:

Type Description
SearchWindowExceeded

the next page would cross the 10 000-document result window; use the archive API to read further.

Source code in src/vulners/_pagination.py
def next_page(self) -> SearchPage[T]:
    """Fetch the next page.

    Raises:
        SearchWindowExceeded: the next page would cross the 10 000-document
            result window; use the archive API to read further.
    """
    next_offset = self.offset + self.limit
    if _window_blocked(self.offset, self.limit):
        raise SearchWindowExceeded(
            f"cannot page past offset {SEARCH_WINDOW}: the search window is "
            "capped at 10000 documents. Use the archive API to retrieve more."
        )
    if self.fetch is None:
        raise RuntimeError("this page was not built with a fetch callback")
    return self.fetch(next_offset, self.limit)

vulners._pagination.AsyncSearchPage dataclass

AsyncSearchPage(data: list[T] = list(), total: int | None = None, offset: int = 0, limit: int = 0, fetch: AsyncFetch[T] | None = None)

Bases: Generic[T]

Async counterpart of :class:SearchPage.

has_next_page

has_next_page() -> bool

True when a further page exists and stays within the 10k window.

Source code in src/vulners/_pagination.py
def has_next_page(self) -> bool:
    """True when a further page exists and stays within the 10k window."""
    if self.fetch is None or _window_blocked(self.offset, self.limit):
        return False
    return not _short_page(len(self.data), self.limit, self.offset, self.total)

next_page async

next_page() -> AsyncSearchPage[T]

Fetch the next page.

Raises:

Type Description
SearchWindowExceeded

the next page would cross the 10 000-document result window; use the archive API to read further.

Source code in src/vulners/_pagination.py
async def next_page(self) -> AsyncSearchPage[T]:
    """Fetch the next page.

    Raises:
        SearchWindowExceeded: the next page would cross the 10 000-document
            result window; use the archive API to read further.
    """
    next_offset = self.offset + self.limit
    if _window_blocked(self.offset, self.limit):
        raise SearchWindowExceeded(
            f"cannot page past offset {SEARCH_WINDOW}: the search window is "
            "capped at 10000 documents. Use the archive API to retrieve more."
        )
    if self.fetch is None:
        raise RuntimeError("this page was not built with a fetch callback")
    return await self.fetch(next_offset, self.limit)

Client configuration

The resolved, immutable configuration for a client instance is available as client.config.

vulners._config.ClientConfig dataclass

ClientConfig(api_key: SecretStr, base_url: URL, user_agent: str, timeout: Timeout = (lambda: DEFAULT_TIMEOUT)(), archive_timeout: Timeout = (lambda: ARCHIVE_TIMEOUT)(), max_retries: int = DEFAULT_MAX_RETRIES, connect_retries: int = DEFAULT_CONNECT_RETRIES, limits: Limits = (lambda: DEFAULT_LIMITS)(), max_rate_limit_wait: float = DEFAULT_MAX_RATE_LIMIT_WAIT, max_response_bytes: int | None = None, follow_redirects: bool = True, http2: bool = True, proxy: str | Proxy | None = None, verify: bool | str | SSLContext = True, trust_env: bool = True, before_request: tuple[Callable[..., Any], ...] = (), after_response: tuple[Callable[..., Any], ...] = (), on_error: tuple[Callable[..., Any], ...] = ())

Immutable resolved configuration for a client instance.

Copy-with-overrides via :meth:replace backs client.with_options(...).