# XHS-Downloader: a self-hosted RedNote (Xiaohongshu) link extractor and media collector

> XHS-Downloader is a Python 3.12 tool that turns Xiaohongshu (RedNote) post links into saved images, videos, livePhoto files and JSON metadata. It ships as a desktop program, a Docker image, a TUI, an API server and an MCP server, and it is licensed GPL-3.0.

**JoeanAmier/XHS-Downloader** — 小红书（XiaoHongShu、RedNote）链接提取/作品采集工具

- Repository: https://github.com/JoeanAmier/XHS-Downloader
- Website: https://discord.com/invite/ZYtmgKud9Y
- Stars: 12,877 · Forks: 1,898
- Language: JavaScript
- License: GPL-3.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/joeanamier-xhs-downloader

## What XHS-Downloader actually does, and who it is for

Xiaohongshu, also written XiaoHongShu and branded RedNote, is a Chinese image and video sharing platform. Its post pages are not designed to hand you a file. XHS-Downloader exists to close that gap: it takes a post link, resolves the post, and writes the media and its metadata into a folder on your own machine. The README lists the program features plainly: collect post information, extract download addresses, download post files, download video cover files, download livePhoto files, skip already downloaded files, persist metadata to a file, and record downloaded post IDs.

The audience is narrow but real. It suits someone archiving their own published posts, a researcher keeping a documented copy of public posts, or a developer who wants the extraction logic behind an HTTP endpoint. It is not aimed at casual phone users. There is no Android build in the repository, and the README points Mac OS and Windows 10+ users at a packaged archive rather than an app store listing.

One design decision is worth noting up front. The README treats the Cookie as optional but not neutral: it states that without a Cookie, video posts can only be downloaded at low resolution, and that setting a Cookie gets higher quality without logging into an account. That is a real trade-off, not a footnote.

## Link in, files and JSON out: the collection pipeline

The input surface is defined by link shape. The README lists four accepted forms: an explore URL with a post ID and xsec_token, a discovery/item URL, a user profile URL that embeds a post ID, and a shortened xhslink.com share code. Multiple links can be pasted at once separated by spaces, and the README says the program extracts the valid ones itself.

The repository layout shows how that is implemented. Source lives under source/, the entry point is main.py, and the dependencies in pyproject.toml explain the moving parts: curl-cffi for HTTP, lxml for parsing, aiosqlite for a local database, aiofiles for writing, pyperclip for clipboard access, textual for the terminal interface, pywebview for the desktop window, and fastapi with fastmcp and uvicorn for the server modes. The Dockerfile confirms the runtime shape: it installs requirements.txt into a builder stage, copies source, static and locale into a python:3.12-slim-bookworm image, exposes port 5556, and declares /app/Volume as the volume. The default container command is python main.py TUI.

State is kept in two places. Downloaded post IDs are recorded so repeat runs skip files, and post information is persisted to disk. The README also describes an integrity handling mechanism for files, which matters when a download is interrupted. The volume mount is the practical consequence: if you do not map /app/Volume, your downloads and records live only inside the container.

## Installing XHS-Downloader and running a first collection

The README offers three routes. If you only want files, it recommends the packaged program or Docker. If you want to modify behaviour, it recommends running from source. Source mode needs Python 3.12 or newer, which matches requires-python = ">=3.12" in pyproject.toml.

The recommended source setup uses uv. Run these two commands in the project root:

```bash
uv sync --no-dev
uv run main.py
```

The first resolves and installs dependencies from the lock file; the second starts the program. If you prefer pip, the README gives this sequence, including the Tsinghua mirror it uses for the install:

```bash
python -m venv venv
.\venv\Scripts\activate.ps1
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
python .\main.py
```

On macOS and Linux the activation line differs; use venv/Scripts/activate or source venv/bin/activate as appropriate. Once the program is running, paste one or more post links into the input field, separated by spaces, and it will extract the valid ones and start downloading. Files land under the Volume folder next to the program, and settings live in Volume/settings.json when you use the packaged build.

If you would rather not install Python, the Docker route is shorter. The README gives two pull options and then a container for each mode:

```bash
docker pull joeanamier/xhs-downloader
docker run --name xhs -p 5556:5556 -v xhs_downloader_volume:/app/Volume -it joeanamier/xhs-downloader
```

That starts the TUI. For the API server, append python main.py api to the same command; for the MCP server, append python main.py mcp. The named volume keeps downloads and records across container restarts. Be aware of the documented gap: Docker mode does not support the command line invocation mode, and the clipboard reading and clipboard monitoring features do not work there, though pasting content into the interface does.

## Where XHS-Downloader breaks, and when it is the wrong tool

The most honest limitation is structural. This is a scraper for a platform the project does not control. Link formats, tokens and page structure can change without notice, and when they do, extraction stops until the project catches up. The release history shows the cadence that implies: 2.6 in August 2025, 2.7 in February 2026, 2.8 in September 2026. Between releases, a broken extraction path stays broken. If your workflow cannot tolerate that, a tool you host is a worse fit than one you can simply wait out.

The second limitation is quality gating. The README states that without a Cookie, video posts download at low resolution only. If you need the highest available video quality, you are configuring a Cookie, which means handling a credential that belongs to an account. The README notes no account login is required to obtain higher quality, but the Cookie is still a secret you now store on disk or pass into a container.

The third is platform scope. The repository contains a Dockerfile, a setup.py that builds Windows executables and a GUI executable, and a Tampermonkey userscript, but no Android application. Searches for an APK or an Android app are looking for something this project does not ship.

Finally, the licence. GPL-3.0 is a copyleft licence. If you plan to embed this code in a product you distribute, the licence terms apply to that distribution. That is a constraint to read, not a detail to skip.

## How it compares with yt-dlp and browser userscripts

The obvious alternative for people who already download media is yt-dlp. The difference is scope and origin. yt-dlp is a general extractor built around a large set of site-specific extractors, and it is typically driven from the command line. XHS-Downloader is single-platform and ships a desktop GUI, a TUI, an HTTP API and an MCP server on top of the same core. If your queue mixes Xiaohongshu with other sites, yt-dlp's breadth is the point. If you want a RedNote-specific tool with a window, a terminal interface and an endpoint your own code can call, XHS-Downloader covers ground yt-dlp does not try to.

The second alternative is the Tampermonkey userscript that this same repository includes. The README lists what it extracts: links from the recommendation feed, from an account's published posts, collections, likes, albums, and search results for both posts and users. That is a different job. The userscript harvests links while you browse; the Python program consumes links and produces files. They complement each other rather than compete, and the userscript route needs no Python at all.

A third option is any of the online downloader sites that turn up in search results. Those ask you to paste a link into someone else's server. XHS-Downloader runs locally, which is the reason to accept its setup cost in the first place.

## Maintenance, upgrades and licence cost

The repository is not archived, and the last push was on 2026-09-20, one day before this writing. Release 2.8 arrived on 2026-09-12, roughly seven months after 2.7. That is a slow but live cadence, and the recent push suggests work continues between tagged releases.

Upgrading is documented in two ways for the packaged build. Either copy the old Volume folder into the new program root, or download and unpack the new version without running it, then copy all files over the old ones. Both preserve your settings and records, which is the reason the Volume folder is separate from the code. For source installs, uv sync --no-dev refreshes dependencies against the lock file, and requirements.txt is autogenerated from pyproject.toml by uv pip compile, so the two stay in step.

The upgrade cost that is not documented is the one that matters most: the README does not describe a migration path for the local database or for the metadata files if their schema changes between versions. Copying Volume forward is the instructed approach, and it is also the approach most likely to surface a format change. Keep a copy before you overwrite.

On licensing, the project is GPL-3.0, declared both in pyproject.toml and in the LICENSE file that the Dockerfile copies into the image. Anyone redistributing a modified version carries the obligations that licence imposes. I am not giving legal advice; read the licence text and, if you are shipping something, get proper counsel.

## Conclusion

Adopt XHS-Downloader if you want a scriptable, self-hosted way to archive RedNote posts you have the right to keep, and if you are comfortable running Python 3.12 or the Docker image. Do not adopt it if you need a browser-only downloader, an Android app, or a way around a private account. Before you commit, verify two things: whether the current build still resolves the links you care about, since the project scrapes a site that changes, and how the missing Cookie affects video resolution, because the README states that without a Cookie video posts only download at low resolution.

## FAQ

### What is XHS-Downloader used for?

It collects Xiaohongshu (RedNote) posts from a link: it extracts the post information, resolves the download addresses, saves the images, video, cover and livePhoto files, and can persist the metadata to a file. It also ships a Tampermonkey userscript that extracts post and user links from feeds, profiles, collections, likes, albums and search results.

### Does XHS-Downloader need a Xiaohongshu account or Cookie?

The README describes the Cookie as a non-mandatory item, but states that without it video posts can only be downloaded at low resolution, and that configuring a Cookie gets higher quality without logging into an account. It also suggests configuring or updating the Cookie if features misbehave.

### Can XHS-Downloader run in Docker?

Yes. The README gives docker pull joeanamier/xhs-downloader and a matching ghcr.io image, then a docker run command that maps a host port to 5556 and mounts a volume at /app/Volume. The container defaults to TUI mode; appending python main.py api or python main.py mcp switches it to the API or MCP server. Docker mode does not support the command line invocation mode, and clipboard reading and monitoring do not work there.

### Is there an XHS-Downloader APK for Android?

No. The repository contains a Dockerfile, a setup.py that builds Windows executables and a GUI executable, and a Tampermonkey userscript, but no Android application. The README directs Mac OS and Windows 10 and above users to the packaged program archive.

### Which Python version does XHS-Downloader require?

Python 3.12 or newer. The README says to install an interpreter not lower than 3.12, and pyproject.toml declares requires-python = ">=3.12".

## Sources

- [JoeanAmier/XHS-Downloader on GitHub](https://github.com/JoeanAmier/XHS-Downloader)
- [License: GPL-3.0](https://github.com/JoeanAmier/XHS-Downloader/blob/master/LICENSE)
- [Project website](https://discord.com/invite/ZYtmgKud9Y)
- [README](https://github.com/JoeanAmier/XHS-Downloader/blob/master/README.md)
- [Releases](https://github.com/JoeanAmier/XHS-Downloader/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/joeanamier-xhs-downloader
