Skip to content

Module-level access

vlrdevapi

vlrdevapi — A Python library to scrape data from vlr.gg.

Provides a synchronous, typed interface for accessing match listings, event data, tournament info, team/player profiles, and statistics from vlr.gg.

Examples:

Module-level access (using a default client):

>>> import vlrdevapi
>>> info = vlrdevapi.team.info(team_id=4568)
>>> info.name
'Sentinels'

Explicit client with context manager:

>>> with vlrdevapi.VLRClient() as client:
...     result = client.series.vods(123)

Curried access (pre-bound ID):

>>> matches = vlrdevapi.team(4568).completed_matches()
>>> matches.matches[0].score
'2-1'

Attributes

client module-attribute

Create an explicit VLRClient for connection management.

Returns:

Type Description
type[VLRClient]

The VLRClient class, callable with optional configuration

type[VLRClient]

arguments (timeout, max_retries, etc.) to create a

type[VLRClient]

new client instance.

Examples:

>>> client = vlrdevapi.client()
>>> info = client.series.info(series_id=1)
>>> with vlrdevapi.client() as c:
...     result = c.series.vods(123)

event module-attribute

Access event/tournament data: info, stages, teams, matches, standings, listings.

Returns:

Type Description
EventNamespace

EventNamespace instance bound to the default client.

Examples:

>>> info = vlrdevapi.event.info(event_id=123)
>>> matches = vlrdevapi.event(123).matches(state="upcoming")
>>> standings = vlrdevapi.event(123).standings(stage="group_a")

matches module-attribute

Access match listings: upcoming, live, and completed matches.

Returns:

Type Description
MatchesNamespace

MatchesNamespace instance bound to the default client.

Examples:

>>> upcoming = vlrdevapi.matches.upcoming()
>>> live = vlrdevapi.matches.live()
>>> completed = vlrdevapi.matches.completed()

player module-attribute

Access player data: info, teams, agents, match history, and profile.

Returns:

Type Description
PlayerNamespace

PlayerNamespace instance bound to the default client.

Examples:

>>> info = vlrdevapi.player.info(player_id=11225)
>>> matches = vlrdevapi.player(11225).matches(limit=20)
>>> agents = vlrdevapi.player(11225).agents(timespan="60d")

series module-attribute

Access series/match data: info, vods, players, rounds, performance, economy.

Returns:

Type Description
SeriesNamespace

SeriesNamespace instance bound to the default client.

Examples:

>>> info = vlrdevapi.series.info(series_id=1)
>>> players = vlrdevapi.series(1).players(game_id="all")
>>> rounds = vlrdevapi.series(1).rounds(game_id=1)

team module-attribute

Access team data: info, roster, stats, matches, transactions, placements.

Returns:

Type Description
TeamNamespace

TeamNamespace instance bound to the default client. Call with a

TeamNamespace

team_id for curried access, or use the sub-namespace properties

TeamNamespace

directly (e.g., vlrdevapi.team.info(team_id=4568)).

Examples:

>>> info = vlrdevapi.team.info(team_id=4568)
>>> stats = vlrdevapi.team(4568).stats(last_days=90)
>>> roster = vlrdevapi.team(4568).roster()

Classes

VLRClient

Synchronous client for scraping data from vlr.gg.

Provides access to namespace attributes for players, series, matches, teams, and events.

Examples:

>>> with VLRClient() as client:
...     info = client.series.info(series_id=1)
>>> client = VLRClient(max_retries=5, base_delay=2.0,
...     backoff=BackoffStrategy.LINEAR)
>>> client = VLRClient(requests_per_second=2.0)

Parameters:

Name Type Description Default
base_url str

Base URL for vlr.gg. Defaults to "https://www.vlr.gg".

BASE_URL
headers dict[str, str] | None

Additional HTTP headers to merge with defaults.

None
timeout int

Request timeout in seconds. Defaults to 15.

DEFAULT_TIMEOUT
max_retries int

Maximum number of retry attempts after the initial request. Defaults to 3 (4 total attempts).

3
base_delay float

Base delay in seconds between retries. The actual delay depends on the backoff strategy. Defaults to 1.0.

1.0
backoff BackoffStrategy

Backoff strategy for calculating delay between retries. Defaults to BackoffStrategy.EXPONENTIAL.

EXPONENTIAL
requests_per_second float

Rate limit in requests per second. 0 means unlimited. Defaults to 3.

DEFAULT_RATE_LIMIT
source_tz str | ZoneInfo | tzinfo | None

Timezone VLR.gg uses to render datetimes for this client. When None, datetimes fall back to UTC unless auto_detect_tz is enabled.

None
auto_detect_tz bool

When True and source_tz is not provided, fetch a reference match on init to detect the viewer timezone. Defaults to False (opt-in) to avoid a hidden network call in __init__.

False
**httpx_kwargs Any

Additional keyword arguments passed to httpx.Client.

{}
Source code in vlrdevapi/_client.py
class VLRClient:
    """Synchronous client for scraping data from vlr.gg.

    Provides access to namespace attributes for players, series, matches,
    teams, and events.

    Examples:
        >>> with VLRClient() as client:
        ...     info = client.series.info(series_id=1)

        >>> client = VLRClient(max_retries=5, base_delay=2.0,
        ...     backoff=BackoffStrategy.LINEAR)

        >>> client = VLRClient(requests_per_second=2.0)

    Args:
        base_url: Base URL for vlr.gg. Defaults to ``"https://www.vlr.gg"``.
        headers: Additional HTTP headers to merge with defaults.
        timeout: Request timeout in seconds. Defaults to ``15``.
        max_retries: Maximum number of retry attempts after the initial request.
            Defaults to ``3`` (4 total attempts).
        base_delay: Base delay in seconds between retries. The actual delay
            depends on the ``backoff`` strategy. Defaults to ``1.0``.
        backoff: Backoff strategy for calculating delay between retries.
            Defaults to ``BackoffStrategy.EXPONENTIAL``.
        requests_per_second: Rate limit in requests per second. ``0`` means
            unlimited. Defaults to ``3``.
        source_tz: Timezone VLR.gg uses to render datetimes for this client.
            When ``None``, datetimes fall back to UTC unless
            ``auto_detect_tz`` is enabled.
        auto_detect_tz: When ``True`` and ``source_tz`` is not provided, fetch
            a reference match on init to detect the viewer timezone. Defaults
            to ``False`` (opt-in) to avoid a hidden network call in
            ``__init__``.
        **httpx_kwargs: Additional keyword arguments passed to ``httpx.Client``.

    """

    def __init__(
        self,
        base_url: str = BASE_URL,
        headers: dict[str, str] | None = None,
        timeout: int = DEFAULT_TIMEOUT,
        max_retries: int = 3,
        base_delay: float = 1.0,
        backoff: BackoffStrategy = BackoffStrategy.EXPONENTIAL,
        requests_per_second: float = DEFAULT_RATE_LIMIT,
        source_tz: str | ZoneInfo | tzinfo | None = None,
        auto_detect_tz: bool = False,
        **httpx_kwargs: Any,
    ) -> None:
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.retry_config = RetryConfig(
            max_retries=max_retries,
            base_delay=base_delay,
            backoff=backoff,
        )
        self._rate_limiter = RateLimiter(requests_per_second) if requests_per_second > 0 else None
        merged_headers = {**DEFAULT_HEADERS, **(headers or {})}

        self._client = httpx.Client(
            base_url=self.base_url,
            headers=merged_headers,
            follow_redirects=True,
            **httpx_kwargs,
        )

        if isinstance(source_tz, str):
            self._source_tz: ZoneInfo | tzinfo | None = ZoneInfo(source_tz)
        else:
            self._source_tz = source_tz

        if self._source_tz is None and auto_detect_tz:
            self._source_tz = self._detect_timezone()

        self.player = PlayerNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
        self.series = SeriesNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
        self.matches = MatchesNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
        self.team = TeamNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)
        self.event = EventNamespace(self._client, self.timeout, self.retry_config, self._rate_limiter, merged_headers, self._source_tz)

    def _detect_timezone(self) -> ZoneInfo | tzinfo | None:
        """Detect the viewer timezone VLR.gg renders for this client session."""
        try:
            html = fetch_sync(
                self._client,
                REFERENCE_MATCH_PATH,
                self.timeout,
                retry_config=self.retry_config,
                rate_limiter=self._rate_limiter,
            )
            return detect_vlr_timezone(html)
        except (HTTPError, NotFoundError, RateLimitError, RequestError) as exc:
            logger.warning("Failed to auto-detect VLR timezone: %s", exc)
            return None

    def close(self) -> None:
        """Close the underlying HTTP client and release resources."""
        self._client.close()

    def __enter__(self) -> "VLRClient":
        return self

    def __exit__(self, *exc: Any) -> None:
        self.close()
Methods:
close()

Close the underlying HTTP client and release resources.

Source code in vlrdevapi/_client.py
def close(self) -> None:
    """Close the underlying HTTP client and release resources."""
    self._client.close()