nyora is the official Python SDK for Nyora — a thin
cloud client that gives you 363 live, health-checked sources across 40 languages
through one typed API: search, browse, read chapters, download .cbz, and sync a
library across devices. No scraper to maintain, no JVM, no parser engine on your
machine — pip install nyora and you're reading in 60 seconds.
In one line: Nyora is the programmatic, cross-platform Tachiyomi / Mihon / Kotatsu alternative — a manga API and reader SDK you can
import. If you're building a manga reader, a scanlation bot, a downloader, or a library manager in Python, this is the fastest way to get hundreds of working sources without writing or maintaining a single scraper.
- Why Nyora
- Install
- Quickstart — a working reader in 10 lines
- Core concepts
- API reference
- Recipes
- Chapter ordering (ascending vs descending sources)
- Cloud sync — a library across devices
- Command line (
nyora-cli) - Interactive terminal reader (TUI)
- How it works
- FAQ
- Ecosystem
| 📚 363 working sources | Every source is live health-checked; 597 dead or Cloudflare-walled ones are auto-hidden, so list()/catalog() return only sources that actually work — 363 across 40 languages (268 all-ages, 95 mature). |
| 🌐 One typed API | Source, Manga, MangaChapter, MangaPage, MangaDetails dataclasses. Full type hints, py.typed, mypy-clean. |
| ☁️ Cloud-powered | The kotatsu-parsers engine runs server-side. You get parsed results, not scraping headaches — nothing to compile, no JVM, no Node. |
| 🔀 Correct chapter order | Built-in next_chapter() / previous_chapter() work on both ascending (MangaDex 0→N) and descending (scanlation N→0) sources — no off-by-one "next goes backwards" bug. |
| 🧰 Batteries included | A CLI, an interactive terminal reader (TUI), a .cbz downloader, and cross-device cloud sync — all in one pip install. |
| ⚡ Sync & async | Use Nyora or AsyncNyora with identical APIs. |
pip install nyora # library + CLI + JSON output
pip install "nyora[tui]" # + the interactive terminal readerRequires Python 3.10+. Nothing else — the parser engine lives in the cloud.
import nyora
with nyora.Nyora() as client:
source = client.sources.find("mangadex") # pick any of 363 sources
hits = client.manga.search(source.id, "frieren") # search it
manga = hits.entries[0]
details = client.manga.details(source.id, manga.url, title=manga.title)
first = details.reading_order()[0] # earliest chapter, order-safe
for page in client.manga.pages(source.id, first.url, branch=first.branch):
print(page.url) # image URLs, ready to renderThat's a complete read path: source → search → details → chapter → page images. Point an image widget (Pillow, a GUI, a web frontend) at those URLs and you have a reader.
A source exposes manga; a manga has chapters; a chapter has pages. Every step is one call.
Nyora ─┬─ sources → Source (a content site: MangaDex, Bato, …)
├─ manga → Manga (a series: title, cover, authors, tags)
│ → MangaDetails (Manga + its MangaChapter list)
│ → MangaChapter (id, title, number, url, branch, uploadDate)
│ → MangaPage (a single image url)
├─ library → favourites, history, bookmarks (local or synced)
└─ downloads → offline chapter management
Everything is a plain dataclass — dataclasses.asdict() to serialise, full type hints throughout.
nyora.Nyora(base_url=None, *, timeout=60.0) # sync client (context manager)
nyora.AsyncNyora(base_url=None, *, timeout=60.0) # async client, identical API with awaitbase_url defaults to the public Nyora cloud (https://api.hasanraza.tech). Attributes:
client.sources, client.manga, client.library, client.downloads. Also client.health().
| Method | Returns | Description |
|---|---|---|
list() |
list[Source] |
Installed/loaded sources (dead ones hidden). |
catalog() |
list[Source] |
Every available source (dead ones hidden). |
find(query) |
Source |
First source whose id or name matches (case-insensitive). |
filters(source_id) |
list[SourceFilter] |
A source's supported search filters. |
refresh() / install(id) / uninstall(id) / pin(id) |
— | Manage the loaded set. |
| Method | Returns | Description |
|---|---|---|
popular(source_id, page=1) |
SearchPage |
Popular titles from a source. |
latest(source_id, page=1) |
SearchPage |
Recently updated titles. |
search(source_id, query, page=1) |
SearchPage |
Search one source. |
global_search(query, *, limit_per_source=8) |
list[GlobalSearchGroup] |
Search many sources at once. |
details(source_id, url, *, title=None) |
MangaDetails |
Full metadata + chapter list. |
pages(source_id, chapter_url, *, branch=None) |
list[MangaPage] |
A chapter's image pages. |
suggestions() / alternatives(title) |
list[Manga] / list[dict] |
Recommendations / cross-source matches. |
SearchPage has .entries: list[Manga] and .has_next_page: bool for pagination.
nyora.next_chapter(chapters, current) # -> MangaChapter | None
nyora.previous_chapter(chapters, current) # -> MangaChapter | None
nyora.reading_order(chapters) # -> list, earliest-first
nyora.chapter_reading_delta(chapters) # -> +1 (ascending) or -1 (descending)
# convenience methods on MangaDetails:
details.next_chapter(chapter) / details.previous_chapter(chapter) / details.reading_order()history(), record_history(...), favourites(), toggle_favourite(id), is_favourite(id),
bookmarks(), add_bookmark(...), remove_bookmark(...).
Browse popular with pagination
page = client.manga.popular(source.id, page=1)
while True:
for m in page.entries:
print(m.title)
if not page.has_next_page:
break
page = client.manga.popular(source.id, page=page.number + 1)Search every source at once
for group in client.manga.global_search("solo leveling"):
print(group.source.name, "→", [m.title for m in group.entries])Download a chapter as a .cbz
from nyora.cli import _download_pages
from pathlib import Path
pages = client.manga.pages(source.id, chapter.url, branch=chapter.branch)
_download_pages(pages, Path("solo-leveling-ch1"))Async
import asyncio, nyora
async def main():
async with nyora.AsyncNyora() as client:
src = await client.sources.find("mangadex")
page = await client.manga.popular(src.id)
print([m.title for m in page.entries])
asyncio.run(main())Different sources return chapters in different orders — MangaDex lists oldest-first
(0 → N), many scanlation sites list newest-first (N → 0). A naive chapters[i+1]
"next chapter" therefore goes backwards on half of all sources. Nyora detects the
direction from the chapter numbers so navigation is always correct:
current = details.chapters[3]
nxt = details.next_chapter(current) # always the LATER chapter, any source order
prv = details.previous_chapter(current) # always the EARLIER chapterfrom nyora.sync import NyoraSync
sync = NyoraSync()
sync.sign_in("you@example.com", "password") # or register(...)
sync.upsert("nyora_favourite", [{ "manga_id": manga.url, "sort_key": 0 }])
favs = sync.select("nyora_favourite") # syncs across every Nyora appSame account and library as the Nyora apps on Android, iOS, macOS, Windows, Linux and web.
nyora-cli sources --search mangadex # list/filter sources
nyora-cli popular -s MANGADEX # popular titles
nyora-cli search -s MANGADEX "frieren" # search
nyora-cli details -s MANGADEX <manga-url> # metadata + chapters
nyora-cli pages -s MANGADEX <chap-url> # page image URLs
nyora-cli download -s MANGADEX <chap-url> # save a .cbz
nyora-cli --json popular -s MANGADEX # machine-readable outputBoth nyora and nyora-cli are installed. Add --json to any command for scripting.
pip install "nyora[tui]"
nyora # launch the full-screen reader — browse, search, read, downloadPick a source → search or browse → open a chapter → page through it, with order-independent next / previous chapter navigation and inline downloads.
nyora is a thin cloud client. It speaks a small typed REST API to the public
Nyora cloud helper (https://api.hasanraza.tech) — the kotatsu-parsers JVM engine
with hundreds of sources — so there is nothing to compile and no parser engine, JVM,
Node.js, or bundle on your machine. Dead and Cloudflare-blocked sources are filtered
out client-side from a periodically refreshed health-check, so you only ever see the
363 sources that actually return content. Self-host the helper and point base_url
at it if you'd rather run your own.
How do I build a manga reader in Python?
pip install nyora, then search → details → pages (see Quickstart).
client.manga.pages(...) returns image URLs you can render in any UI.
What's the best manga API / SDK? Nyora gives you 363 working, health-checked sources across 40 languages behind one typed Python API — no scraper maintenance, plus a CLI, TUI, downloader and cloud sync.
Is this a Tachiyomi / Mihon / Kotatsu alternative? Yes — it's the programmatic one. Those are Android apps; Nyora is an importable SDK (and cross-platform apps) built on the same open-source Kotatsu parser engine.
Do I need to run a server or a JVM?
No. The engine is hosted. pip install and go. You can self-host and set base_url.
Manga, manhwa or manhua? All three — the sources cover Japanese, Korean and Chinese comics across 40 languages.
JavaScript / TypeScript? Use the sibling SDK: nyora-sdk.
- JS/TS SDK —
nyora-sdk(npm i nyora-sdk) - Apps — Android, iOS/iPadOS, macOS, Windows, Linux and a web app: https://nyora.pages.dev
- Docs — https://nyora.pages.dev/docs/python/
- Source — https://github.com/Hasan72341/nyora-python
Apache-2.0. Nyora is built on the open-source Kotatsu parser engine and is not affiliated with Tachiyomi, Mihon or Kotatsu.