Quickstart¶
The client¶
All requests go through one AniLiberty instance. Use it as an async
context manager so its connection pool is closed at the end:
from aniliberty import AniLiberty
async with AniLiberty() as client:
...
Without async with, call await client.aclose() when you're done. A closed client can't be
reused — create a new one.
The API is grouped into namespaces that follow the tags of its OpenAPI spec:
| Namespace | What's in it |
|---|---|
client.app |
API status, quick search |
client.releases |
latest, random and recommended releases; a release by ID or alias; its team members; several releases at once |
client.episodes |
an episode with its HLS playlists |
client.catalog |
the release catalog with filters, sorting and pagination — see Catalog filters |
client.genres |
genres with images and counts, random genres, the releases of a genre |
client.franchises |
franchises — related releases in watch order, and the franchise of a release |
client.torrents |
torrents of a release, .torrent downloads, RSS feeds |
client.teams |
AniLibria's own teams, roles and members |
client.media |
front-page promo banners and the latest videos from AniLibria's YouTube playlists |
client.schedule |
the airing schedule — yesterday/today/tomorrow and the whole week |
client.catalog.references |
the values catalog filters accept: genres, types, seasons, years, age ratings, statuses, sorting |
client.http |
the low-level client — request(...) returns raw JSON for endpoints not covered yet, request_raw(...)/stream(...) for files and feeds |
A tour¶
import asyncio
from aniliberty import AniLiberty, NotFoundError
async def main() -> None:
async with AniLiberty() as client:
status = await client.app.status()
print("API alive:", status.is_alive)
# The latest releases, each with its newest episode.
for release in await client.releases.latest(limit=3):
episode = release.latest_episode
number = episode.ordinal if episode else "-"
print(f"{release.name.main} — episode {number}")
# Quick search by Russian or English name, or alias.
found = await client.app.search_releases("frieren")
print("Found:", [r.name.main for r in found])
# A single release by ID or alias, with all its episodes.
release = await client.releases.get("sousou-no-frieren")
print(release.name.english, release.year, release.type.description if release.type else "")
print("Poster:", release.poster.preview_url if release.poster else None)
# An episode's HLS playlists. Not every episode has 1080p.
episode = await client.episodes.get(release.episodes[0].id)
print("Playlist:", (episode.hls_1080 or episode.hls_720 or "")[:60], "...")
try:
await client.releases.get("no-such-release")
except NotFoundError as error:
print("Not found:", error.status_code)
asyncio.run(main())
Output of a real run (2026-10-16):
API alive: True
О моём перерождении в меч 2 — episode 3
Во всеоружии: Сила артефактов — episode 3
Операция: Семейка Ёдзакура 2. Часть 2 — episode 1
Found: ['Провожающая в последний путь Фрирен', 'Провожающая в последний путь Фрирен 2']
Sousou no Frieren 2023 ТВ
Poster: https://aniliberty.top/storage/releases/posters/9542/PaDTDJMU3ybqwWr5BRXydhSM8qbwIhBd.jpg
Playlist: https://cache.libria.fun/videos/media/ts/9542/1/1080/aa675e5 ...
Not found: 404
Things worth knowing¶
Value + description fields. Enumerations such as a release's type, season or publish day
are ValueDescription objects: compare .value
("TV", "autumn", 5), show .description ("ТВ", "Осень", "Пятница"). Optional ones
can be None.
Images. The API sends relative paths; Image keeps them in
preview/thumbnail/src and gives absolute URLs in preview_url/thumbnail_url/src_url,
on the host the client talks to. image.optimized has the same image in WebP.
HLS links are returned exactly as the API sends them — the query string is part of the
link. The SDK doesn't download video; hand the playlist to a player or to ffmpeg.
Pagination. Endpoints with pages return a Page (items, page,
total_pages, has_next_page, ...) and have an iter_*() twin that walks all pages for you
(more in the Pagination guide):
async for release in client.releases.iter_list(ids=[9542, 10172, 10297], limit=50):
print(release.name.main)
Limits. limit on the release endpoints is 1 to 50; anything else is rejected by the API
with a ValidationError.
Errors. Everything the SDK raises on purpose is an
AniLibertyError. Rate limits (429), server errors (5xx) and
network errors are retried automatically before an exception reaches you — see
Errors and retries.
Mirrors. The default host is https://aniliberty.top/api/v1; https://anilibria.top/api/v1
serves the same API (more in Mirrors and a custom HTTP client):
async with AniLiberty(base_url="https://anilibria.top/api/v1") as client:
...
Concurrency. asyncio.gather over many calls is fine: the client itself keeps at most
max_concurrency (default 5) requests in flight — see Concurrency.
releases = await asyncio.gather(*(client.releases.get(i) for i in (9542, 10172, 10297)))
Logging. The SDK logs to the aniliberty logger: every request at DEBUG, retries at
INFO.