Skip to content

Clients

Nyaa

Nyaa(
    *,
    base_url: str = "https://nyaa.si/",
    client: Client | None = None,
)

Client for interacting with Nyaa.

Parameters:

Name Type Description Default
base_url str

Base URL of Nyaa. Used to construct full URLs from relative URLs.

'https://nyaa.si/'
client Client

Custom httpx.Client instance.

None
Source code in src/pynyaa/_client.py
def __init__(self, *, base_url: str = "https://nyaa.si/", client: httpx.Client | None = None) -> None:
    """
    Client for interacting with Nyaa.

    Parameters
    ----------
    base_url : str, optional
        Base URL of Nyaa.
        Used to construct full URLs from relative URLs.
    client : httpx.Client, optional
        Custom [`httpx.Client`](https://www.python-httpx.org/api/#client) instance.

    """
    self._base_url = base_url
    self._client = (
        httpx.Client(headers={"User-Agent": f"pynyaa/{__version__} (https://pypi.org/project/pynyaa/)"})
        if client is None
        else client
    )

base_url property

base_url: str

Base URL of Nyaa, used to construct full URLs from relative URLs.

close

close() -> None

Close the underlying HTTP client session.

Source code in src/pynyaa/_client.py
def close(self) -> None:
    """
    Close the underlying HTTP client session.
    """
    self._client.close()

get

get(page: int | str) -> NyaaRelease

Fetch metadata for a specific Nyaa release.

Parameters:

Name Type Description Default
page int or str

Release ID or full URL (e.g., 123456 or https://nyaa.si/view/123456).

required

Raises:

Type Description
ReleaseNotFoundError

If the release does not exist (HTTP 404).

TypeError

If page is not an int or str.

ValueError

If page is a string but not a valid release URL.

Returns:

Type Description
NyaaRelease

Parsed release metadata.

Source code in src/pynyaa/_client.py
def get(self, page: int | str, /) -> NyaaRelease:
    """
    Fetch metadata for a specific Nyaa release.

    Parameters
    ----------
    page : int or str
        Release ID or full URL (e.g., `123456` or `https://nyaa.si/view/123456`).

    Raises
    ------
    ReleaseNotFoundError
        If the release does not exist (HTTP 404).
    TypeError
        If `page` is not an `int` or `str`.
    ValueError
        If `page` is a string but not a valid release URL.

    Returns
    -------
    NyaaRelease
        Parsed release metadata.

    """
    match page:
        case int():
            id = page
        case str():
            try:
                id = int(page.rstrip("/").split("/")[-1])
            except ValueError:
                msg = f"Invalid format for 'page'. Expected a valid URL or numeric ID, but got {page!r}."
                raise ValueError(msg) from None
        case _:
            msg = f"Parameter 'page' expected 'int' or 'str', but got {type(page).__name__!r}."
            raise TypeError(msg)

    torrent_page_url = urljoin(self._base_url, f"/view/{id}")
    torrent_file_url = urljoin(self._base_url, f"/download/{id}.torrent")

    torrent_page, torrent_file = (
        self._client.get(torrent_page_url),
        self._client.get(torrent_file_url),
    )

    if httpx.codes.NOT_FOUND in (torrent_page.status_code, torrent_file.status_code):
        raise ReleaseNotFoundError(torrent_page_url)
    torrent_page.raise_for_status()
    torrent_file.raise_for_status()

    parsed = TorrentPageParser(html=torrent_page.text, base_url=self.base_url)

    return NyaaRelease(
        id=id,
        url=torrent_page_url,
        title=parsed.panel.title(),
        category=parsed.panel.category(),
        datetime=parsed.panel.datetime(),
        submitter=parsed.panel.submitter(),
        information=parsed.panel.information(),
        seeders=parsed.panel.seeders(),
        leechers=parsed.panel.leechers(),
        completed=parsed.panel.completed(),
        is_trusted=parsed.is_trusted(),
        is_remake=parsed.is_remake(),
        torrent=TorrentFile(
            name=parse_torrent_filename(torrent_file.headers["Content-Disposition"]),
            data=torrent_file.content,
            size=parsed.panel.size(),
            infohash=parsed.panel.infohash(),
            url=torrent_file_url,
            magnet=parsed.panel.magnet(),
        ),
        description=parsed.description(),
    )

search

search(
    query: str,
    /,
    *,
    category: ParentCategory | Category = ALL,
    filter: Filter = NO_FILTER,
    sort_by: SortBy = DATETIME,
    order: Order = DESCENDING,
) -> Iterator[NyaaRelease]

Search for releases on Nyaa.

Parameters:

Name Type Description Default
query str

Search query string.

required
category ParentCategory | Category

Category or subcategory used to filter results.

ALL
filter Filter

Filter applied to the search results.

NO_FILTER
sort_by SortBy

Field used to sort the results.

DATETIME
order Order

Order of the results.

DESCENDING

Yields:

Type Description
NyaaRelease

Parsed release metadata for each search result.

Source code in src/pynyaa/_client.py
def search(
    self,
    query: str,
    /,
    *,
    category: ParentCategory | Category = ParentCategory.ALL,
    filter: Filter = Filter.NO_FILTER,
    sort_by: SortBy = SortBy.DATETIME,
    order: Order = Order.DESCENDING,
) -> Iterator[NyaaRelease]:
    """
    Search for releases on Nyaa.

    Parameters
    ----------
    query : str
        Search query string.
    category : ParentCategory | Category, optional
        Category or subcategory used to filter results.
    filter : Filter, optional
        Filter applied to the search results.
    sort_by : SortBy, optional
        Field used to sort the results.
    order : Order, optional
        Order of the results.

    Yields
    ------
    NyaaRelease
        Parsed release metadata for each search result.

    """
    assert_type(query, str, "query")
    assert_type(category, (ParentCategory, Category), "category")
    assert_type(filter, Filter, "filter")
    assert_type(sort_by, SortBy, "sort_by")
    assert_type(order, Order, "order")

    params: dict[str, Any] = {
        "f": filter,
        "c": category.id,
        "q": query,
        "s": sort_by,
        "o": order,
    }

    # First page
    first = self._client.get(self._base_url, params=params)
    first.raise_for_status()
    html = first.text
    parsed = SearchPageParser(html)
    for id in parsed.results():
        yield self.get(id)

    for page in parsed.pages():  # Second page onwards
        params["p"] = page
        other = self._client.get(self._base_url, params=params)
        other.raise_for_status()
        html = other.text
        parsed = SearchPageParser(html)
        for id in parsed.results():
            yield self.get(id)

AsyncNyaa

AsyncNyaa(
    *,
    base_url: str = "https://nyaa.si/",
    client: AsyncClient | None = None,
)

Client for interacting with Nyaa.

Parameters:

Name Type Description Default
base_url str

Base URL of Nyaa. Used to construct full URLs from relative URLs.

'https://nyaa.si/'
client AsyncClient

Custom httpx.AsyncClient instance.

None
Source code in src/pynyaa/_aclient.py
def __init__(self, *, base_url: str = "https://nyaa.si/", client: httpx.AsyncClient | None = None) -> None:
    """
    Client for interacting with Nyaa.

    Parameters
    ----------
    base_url : str, optional
        Base URL of Nyaa.
        Used to construct full URLs from relative URLs.
    client : httpx.AsyncClient, optional
        Custom [`httpx.AsyncClient`](https://www.python-httpx.org/api/#asyncclient) instance.

    """
    self._base_url = base_url
    self._client = (
        httpx.AsyncClient(headers={"User-Agent": f"pynyaa/{__version__} (https://pypi.org/project/pynyaa/)"})
        if client is None
        else client
    )

base_url property

base_url: str

Base URL of Nyaa, used to construct full URLs from relative URLs.

close async

close() -> None

Close the underlying HTTP client session.

Source code in src/pynyaa/_aclient.py
async def close(self) -> None:
    """
    Close the underlying HTTP client session.
    """
    await self._client.aclose()

get async

get(page: int | str) -> NyaaRelease

Fetch metadata for a specific Nyaa release.

Parameters:

Name Type Description Default
page int or str

Release ID or full URL (e.g., 123456 or https://nyaa.si/view/123456).

required

Raises:

Type Description
ReleaseNotFoundError

If the release does not exist (HTTP 404).

TypeError

If page is not an int or str.

ValueError

If page is a string but not a valid release URL.

Returns:

Type Description
NyaaRelease

Parsed release metadata.

Source code in src/pynyaa/_aclient.py
async def get(self, page: int | str, /) -> NyaaRelease:
    """
    Fetch metadata for a specific Nyaa release.

    Parameters
    ----------
    page : int or str
        Release ID or full URL (e.g., `123456` or `https://nyaa.si/view/123456`).

    Raises
    ------
    ReleaseNotFoundError
        If the release does not exist (HTTP 404).
    TypeError
        If `page` is not an `int` or `str`.
    ValueError
        If `page` is a string but not a valid release URL.

    Returns
    -------
    NyaaRelease
        Parsed release metadata.

    """
    match page:
        case int():
            id = page
        case str():
            try:
                id = int(page.rstrip("/").split("/")[-1])
            except ValueError:
                msg = f"Invalid format for 'page'. Expected a valid URL or numeric ID, but got {page!r}."
                raise ValueError(msg) from None
        case _:
            msg = f"Parameter 'page' expected 'int' or 'str', but got {type(page).__name__!r}."
            raise TypeError(msg)

    torrent_page_url = urljoin(self._base_url, f"/view/{id}")
    torrent_file_url = urljoin(self._base_url, f"/download/{id}.torrent")

    torrent_page, torrent_file = await asyncio.gather(
        self._client.get(torrent_page_url),
        self._client.get(torrent_file_url),
    )

    if httpx.codes.NOT_FOUND in (torrent_page.status_code, torrent_file.status_code):
        raise ReleaseNotFoundError(torrent_page_url)
    torrent_page.raise_for_status()
    torrent_file.raise_for_status()

    parsed = TorrentPageParser(html=torrent_page.text, base_url=self.base_url)

    return NyaaRelease(
        id=id,
        url=torrent_page_url,
        title=parsed.panel.title(),
        category=parsed.panel.category(),
        datetime=parsed.panel.datetime(),
        submitter=parsed.panel.submitter(),
        information=parsed.panel.information(),
        seeders=parsed.panel.seeders(),
        leechers=parsed.panel.leechers(),
        completed=parsed.panel.completed(),
        is_trusted=parsed.is_trusted(),
        is_remake=parsed.is_remake(),
        torrent=TorrentFile(
            name=parse_torrent_filename(torrent_file.headers["Content-Disposition"]),
            data=torrent_file.content,
            size=parsed.panel.size(),
            infohash=parsed.panel.infohash(),
            url=torrent_file_url,
            magnet=parsed.panel.magnet(),
        ),
        description=parsed.description(),
    )

search async

search(
    query: str,
    /,
    *,
    category: ParentCategory | Category = ALL,
    filter: Filter = NO_FILTER,
    sort_by: SortBy = DATETIME,
    order: Order = DESCENDING,
) -> AsyncIterator[NyaaRelease]

Search for releases on Nyaa.

Parameters:

Name Type Description Default
query str

Search query string.

required
category ParentCategory | Category

Category or subcategory used to filter results.

ALL
filter Filter

Filter applied to the search results.

NO_FILTER
sort_by SortBy

Field used to sort the results.

DATETIME
order Order

Order of the results.

DESCENDING

Yields:

Type Description
NyaaRelease

Parsed release metadata for each search result.

Source code in src/pynyaa/_aclient.py
async def search(
    self,
    query: str,
    /,
    *,
    category: ParentCategory | Category = ParentCategory.ALL,
    filter: Filter = Filter.NO_FILTER,
    sort_by: SortBy = SortBy.DATETIME,
    order: Order = Order.DESCENDING,
) -> AsyncIterator[NyaaRelease]:
    """
    Search for releases on Nyaa.

    Parameters
    ----------
    query : str
        Search query string.
    category : ParentCategory | Category, optional
        Category or subcategory used to filter results.
    filter : Filter, optional
        Filter applied to the search results.
    sort_by : SortBy, optional
        Field used to sort the results.
    order : Order, optional
        Order of the results.

    Yields
    ------
    NyaaRelease
        Parsed release metadata for each search result.

    """
    assert_type(query, str, "query")
    assert_type(category, (ParentCategory, Category), "category")
    assert_type(filter, Filter, "filter")
    assert_type(sort_by, SortBy, "sort_by")
    assert_type(order, Order, "order")

    params: dict[str, Any] = {
        "f": filter,
        "c": category.id,
        "q": query,
        "s": sort_by,
        "o": order,
    }

    # First page
    first = await self._client.get(self._base_url, params=params)
    first.raise_for_status()
    html = first.text
    parsed = SearchPageParser(html)
    for id in parsed.results():
        yield await self.get(id)

    for page in parsed.pages():  # Second page onwards
        params["p"] = page
        other = await self._client.get(self._base_url, params=params)
        other.raise_for_status()
        html = other.text
        parsed = SearchPageParser(html)
        for id in parsed.results():
            yield await self.get(id)