Douyin_TikTok_Download_API README

The project's README from github.com/Evil0ctal/Douyin_TikTok_Download_API. As one markdown file: README.md.

Douyin_TikTok_Download_API

Douyin_TikTok_Download_API

English | 简体中文

🚀 A self-hosted data API for Douyin and TikTok. One docker compose up, an identity pool that maintains itself, and a REST API, MCP server and web console on top.

Open source, free, and it runs on your own machine — no signup, no quota, nobody else in the path. It fetches posts, authors, comments and search, downloads video and image albums without a watermark (it picks the clean stream the platform already publishes rather than stripping anything), and keeps what it collects in your own PostgreSQL.

GitHub license Release Version GitHub Star GitHub forks GitHub issues
CI CodeQL Last commit
Website Live demo Python MCP Docker Pulls Docker Image Size

💖 Sponsors

These sponsors paid to be here, and Douyin_TikTok_Download_API stays free and open because of it. To sponsor the project, see my GitHub Sponsors page.

TikHub.io - Global Social Data & API Marketplace

TikHub.io

Your Ultimate Social Media Data & API Marketplace

Professional data solutions for Douyin, Xiaohongshu, TikTok, Instagram, YouTube, Twitter, and more.
Real-time Data | Flexible APIs | Seamless Integration | Competitive Pricing with Discounts

Buy and sell custom APIs, services, and social media solutions on the
TikHub.io Marketplace, alongside developers, businesses and content creators.

Trusted by leading global influencer marketing and social media intelligence platforms

→ Visit TikHub.io  ·  API docs

🧩 REER — a forum for reverse engineering

REER

reer.dev

Somewhere to put what you work out. This project exists because people wrote down how a signature was built; the forum is for the next round of that — signatures, protocols, packers, anything taken apart.

Free, no advertising, and close to no rules beyond the obvious. Bilingual, English and Chinese. Register and post; nothing is gated.

🎬 What it looks like

The DTK console: overview, identities, scheduler, playground, library, downloads, API docs and MCP

One real call: paste a link, send it, get the normalised result back. The identity pool, the scheduler and the API reference it passed through on the way are all in the same console. The interface follows the browser's language, and both are written by hand rather than machine-translated. 中文界面

Try it without installing anything

https://demo.douyin.wtf is a live instance, open to everyone. Sign in — the login page fills the demo account in for you — and use the console: the playground, the scheduler, the library, the API reference. The same account comes with an API key you can call the scraping endpoints with, shown in plaintext on the API keys page.

It is read-only. Demo requests are not written to the request log or the archive, so the database of a public instance cannot be filled by strangers, and switching demo mode off ends every demo session and kills the key immediately.

Rate limit: 30 requests per 10 seconds. Go over it and you are cooled down for 10 seconds, during which every request is refused. It is a shared instance, and the identity pool behind it is the ceiling for everybody using it at once.

What the demo is for is deciding whether to run your own. It is not a service to build on — it can go down, be reset, or be switched off, and none of that will be announced.

🚀 v4 vs v5

v5 is a rewrite. It started from an empty branch and inherits no v4 code.

v4's real problem was never a shortage of features — it was that the API would die quietly and nobody would know. A cookie expires, a signature algorithm changes, an endpoint gets rate-limited, and you find out when someone files an issue. v5 puts "you can see it" and "it heals itself" ahead of features.

v4 v5
Where identities come from You copy cookies out of a browser into config.yaml A headless browser mints guest identities, and the pool tops itself up when usable ones run low
How requests go out Straight out, as they arrive Health tiers, quantised LRU rotation, one in-flight lock per identity, a token bucket per (identity, endpoint), a circuit breaker per endpoint
When something breaks You wait for a bug report One structured record per request, live health for every identity and endpoint, visible in the console
Call style Synchronous — send and wait Asynchronous by default (202 + task_id); add ?wait= to go back to synchronous
What is kept Nothing; parsed and discarded PostgreSQL + Redis. Everything parsed is archived, so a post deleted upstream is still here
Access control None; anyone can call it API keys with scopes and roles, managed in the console
Interface A single PyWebIO page A React console: identity pool, scheduler, library, downloads, logs, diagnostics
Ways in REST REST, MCP and a CLI, all over the same service layer
Signing X-Bogus, A_Bogus a_bogus, X-Bogus, X-Gnarly, X-Dynosaur in pure Python, with a browser fallback
Deployment pip install -r requirements.txt + python start.py docker compose up, three images
Platforms Douyin, TikTok, Bilibili Douyin, TikTok

Bilibili is the one thing that went backwards: v5 does not have it yet. It shares neither the signing nor the identity machinery with Douyin and TikTok, so the rewrite left it out for now.

Still on v4?

v4's code stays on the v4 branch, the image is still published, and one command brings it up:

docker run -d --name dtk-v4 --restart unless-stopped \
  -p 8080:80 evil0ctal/douyin_tiktok_download_api:V4.1.2

Then open http://localhost:8080; the API reference is at /docs. Port 8080 is deliberate — v5 binds 8000, so the two versions can run side by side on one machine.

Replace the Douyin cookie before you use it. v4 has no identity pool, and the cookie baked into the image expired long ago, so Douyin endpoints answer 400 until you put your own in:

docker cp dtk-v4:/app/crawlers/douyin/web/config.yaml ./douyin-web.yaml
# replace the Cookie line with one from your browser, then save
docker rm -f dtk-v4
docker run -d --name dtk-v4 --restart unless-stopped -p 8080:80 \
  -v "$PWD/douyin-web.yaml:/app/crawlers/douyin/web/config.yaml" \
  evil0ctal/douyin_tiktok_download_api:V4.1.2

TikTok's two config files are at /app/crawlers/tiktok/web/config.yaml and /app/crawlers/tiktok/app/config.yaml, and are mounted the same way.

To keep it under Compose instead, one file is enough — with douyin-web.yaml already copied out next to it, or Docker will create a directory under that name and the app will fail to read its config:

# compose.yml
services:
  app:
    image: evil0ctal/douyin_tiktok_download_api:V4.1.2
    container_name: dtk-v4
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      TZ: Asia/Shanghai
    volumes:
      - ./douyin-web.yaml:/app/crawlers/douyin/web/config.yaml
docker rm -f dtk-v4   # the container from the run above holds the name
docker compose -p dtk-v4 up -d

main is v5 now and latest follows main, so pin the tag to stay on v4. V4.1.2 is the last v4 release; the branch merged a few PRs after it (Bilibili downloads, cookie hot-reload), and that code is published under the commit tag 5be4838.

There is no guided installer for v4 — the script in Quick start below installs v5. What is above is all v4 gets: the branch still takes PRs, but nothing new is built on it, and a fresh install should start on v5.

Building this together

There are a few group chats around my open-source projects. If you want to work on this one, or just talk shop, add me on WeChat at Evil0ctal with the note github 交流 and I will add you.

The groups are for learning from each other. No advertising and nothing illegal — they are for making friends and talking about the work.

📦 What it can fetch from Douyin and TikTok

Capability Douyin TikTok
One post (video or image album) ✅ ✅
Author profile ✅ ✅
An author's posts ✅ ✅
An author's liked posts ✅ ✅
Mixes / playlists ✅ ✅
Comments ✅ ✅
Comment replies ✅ ✅
Followers ❌ ✅
Following ❌ ✅

Douyin serves its follower and following lists only to a signed-in session, so those two endpoints are not registered at all: an endpoint that always returns an empty page is worth nothing. Importing your own logged-in cookies widens what the rest can see, too.

Media downloads, the content archive, counter snapshots, collections and a watchlist are built in; none of them needs another service.

Paste whatever you have — a short link, a post URL, or the whole caption a platform app puts on your clipboard:

https://v.douyin.com/L4NpDJ6/
https://www.douyin.com/video/7126745726494821640
https://www.douyin.com/jingxuan?modal_id=7660875690212492466
https://www.tiktok.com/@evil0ctal/video/7156033831819037994
https://www.tiktok.com/t/ZTR9nkkmL/
2.84 nqe:/ <caption> https://v.douyin.com/L4FJNR3/ <sentence telling you to open the app>

Short links are followed and a link buried in a caption is extracted. A post id is also checked against the platform's own encoding first, so an id that cannot exist is refused here rather than costing an upstream request.

⚗️ Built with

Service Python 3.12 · FastAPI · SQLAlchemy 2.0 (async) · Alembic · Typer · structlog
Transport wreq (browser TLS fingerprint emulation) · httpx
Data PostgreSQL + TimescaleDB · Redis
Console React 19 · TypeScript · Vite · TanStack Query · wouter · i18next
Signing a_bogus, X-Bogus, X-Gnarly and X-Dynosaur in pure Python
Identity minting CloakBrowser, headless, in a container of its own, called over HTTP
Downloader Go 1.23, standard library only, statically linked into a scratch image
Auth argon2id password hashing · API keys with scopes
Protocols REST (OpenAPI) · MCP (streamable-http) · CLI
Tooling uv · ruff · mypy · pytest · Docker Compose

Nothing beyond Postgres and Redis is required. No Kafka, no Elasticsearch, no object store, no Kubernetes.

CloakBrowser is pinned to a specific commit. That pin is a security control — see docker/Dockerfile.browser.

🗂 Project layout

First, what dtk is. Douyin_TikTok_Download_API is a mouthful, so the code says DTK instead: Douyin + TiKTok. The two platforms, and not the download or API halves of the name.

It is the Python package (src/dtk/), the command (dtk --help), the prefix on every environment variable (DTK_SECRET_KEY and friends), the Compose project name (-p dtk), and the three letters in the console's title bar. Where you see dtk, it means Douyin_TikTok_Download_API.

Douyin_TikTok_Download_API/
├── src/dtk/                the service; all of it lives here
│   ├── api/                FastAPI routes, auth, OpenAPI localisation
│   ├── platforms/          Douyin and TikTok adapters: endpoints, params, parsers
│   ├── signing/            a_bogus / X-Bogus / X-Gnarly / X-Dynosaur
│   ├── transport/          outbound requests, response classification
│   ├── identity/           identity minting and health
│   ├── scheduler/          identity selection, token buckets, circuit breakers
│   ├── services/           the business layer, shared by REST, MCP and the CLI
│   ├── worker/             async tasks, callbacks, scheduled collection
│   ├── db/                 SQLAlchemy models and Alembic migrations
│   ├── ops/                diagnostics, backups, health checks, alerting
│   ├── media/              downloader client
│   ├── models/             one content model across both platforms
│   ├── urls/               link recognition, short-link expansion, id validation
│   ├── mcp/                MCP server
│   ├── cli/                the dtk command line
│   ├── i18n/               server-side English and Chinese strings
│   └── core/               settings, logging, error types
├── web/                    the React console, built into the app image
│   └── src/
│       ├── pages/          one file per console page
│       ├── components/     design system and shared components
│       └── locales/        console English and Chinese strings
├── docker/                 three Dockerfiles, compose, and two sidecars
│   ├── browser_rpc/        Python, wrapping CloakBrowser
│   ├── downloader/         Go, the media download sidecar
│   └── compose.yml
├── documents/              user documentation, 17 pages in each language
├── tests/                  unit / integration / contract / replay
├── scripts/                smoke.sh
├── .github/workflows/      CI and Docker image publishing
├── alembic.ini
├── pyproject.toml
└── Makefile

⚡️ Quick start: self-host it with Docker

If you would rather not think about it, the guided script asks a few questions and brings the stack up — it works out your distribution, checks for Docker, and scales the resource limits to the machine:

curl -fsSL https://raw.githubusercontent.com/Evil0ctal/Douyin_TikTok_Download_API/main/install/install.sh -o install.sh
less install.sh    # reading it first is a good habit
bash install.sh

Afterwards the same script is the operations tool. Run it again and it finds the install and opens a menu: status, upgrade (compared against the latest GitHub release), passwords, an extra administrator, backup and restore, runtime settings, disk cleanup, stop or uninstall. --manage goes straight there.

Details in install/README.md. The manual route follows.

There are two ways to install this; below is the recommended one. For the by-hand route, see Without Docker.

How Who for Full steps
Docker Compose (recommended) Almost everyone, production included Installation · First install
By hand Docker is not an option, or you are changing the code For development · On bare metal

Installing from mainland China? Switch your mirrors before you start, or the pull will most likely time out: Network preparation in mainland China.

You need Docker and Docker Compose. Start by cloning the repository — the compose file, the Dockerfiles and the migrations all live in it, and the default branch is v5:

git clone https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git
cd Douyin_TikTok_Download_API

Nothing in the repository ships a default password or key, so write .env next:

POSTGRES_PASSWORD=$(openssl rand -hex 24)
REDIS_PASSWORD=$(openssl rand -hex 24)
cat > .env <<EOF
DTK_SECRET_KEY=$(openssl rand -base64 48)
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
REDIS_PASSWORD=${REDIS_PASSWORD}
DTK_DATABASE_URL=postgresql+asyncpg://dtk:${POSTGRES_PASSWORD}@postgres:5432/dtk
DTK_REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
EOF
docker compose -p dtk -f docker/compose.yml up -d
docker compose -p dtk -f docker/compose.yml logs api   # prints the setup token

Open http://127.0.0.1:8000 and use the token from the log to create the first administrator.

Where the image comes from

By default it is built locally: the first up compiles the application image from the Dockerfile in this repository, which takes a few minutes. To skip that, point compose at the published image instead:

export DTK_IMAGE=evil0ctal/douyin_tiktok_download_api
export DTK_IMAGE_TAG=latest
docker compose -p dtk -f docker/compose.yml pull
docker compose -p dtk -f docker/co

Douyin_TikTok_Download_API (Evil0ctal/Douyin_TikTok_Download_API)

Searching Database…

这是什么?一个自托管的抖音和 TikTok 数据 API:它可以抓取帖子、作者主页、评论、搜索结果和播放列表,无水印地下载视频和图集,并通过 REST API、MCP 服务器、命令行工具和网页控制台把这些能力全部暴露出来。
这是需要注册使用的在线服务,还是我自己部署运行?由你自己部署运行。它基于 Apache-2.0 协议免费开源,采集、下载、存储等一切操作都在你自己的机器上完成,无需注册,没有配额限制,中间也不经过任何第三方。
我怎么把它跑起来?安装 Docker 和 Docker Compose,克隆仓库(默认分支是 v5),再写一个 .env 文件,因为项目不带任何默认密码或密钥。然后运行 docker compose up,打开控制台,用日志里的 token 创建第一个管理员账号。
开始之前我需要准备好什么?只需要 Docker 和 Docker Compose —— PostgreSQL 和它的数据库迁移都随仓库附带。如果你在中国大陆安装,请先切换镜像源,否则拉取十有八九会超时。
无水印下载有什么限制吗?它并没有去掉水印的处理步骤:API 直接选用平台自己发布的无水印清晰流。也就是说,下载的可靠程度完全取决于平台发布的这条清晰流。
它采集到的数据都存在哪里?存在你自己机器上的 PostgreSQL 里,数据归档始终掌握在你自己手中,而不是放在某个第三方服务上。
身份池是什么,跟我有什么关系?服务内部维护一个身份池,会自动维护和自我修复,因此采集不需要人工维护就能持续运行。它的状态和调度器一样,都可以在网页控制台里查看。
我能用编程代理或大模型工具调用它吗?可以 —— 项目自带 MCP 服务器,支持 MCP 的客户端可以把抓取和下载功能当作工具来用。同一次部署里还提供异步 REST API、命令行工具和网页控制台。
能不能什么都不装就先试试?可以,README 里有一节 'Try it without installing anything':粘贴一个链接就能看到规范化后的结果,同时身份池、调度器以及请求经过的 API 调用链路都会显示在同一个控制台里。
为什么第一次启动要花几分钟?默认情况下,第一次运行 docker compose up 时,应用镜像是从仓库里的 Dockerfile 在本地构建的。想跳过这一步,可以让 compose 使用已发布的镜像:export DTKIMAGE=evil0ctal/douyintiktokdownloadapi,然后直接拉取镜像而不用构建。
Creator
Evil0ctal
Size
22.7 MB
Stars
20.7K
Age
4y
Commits · 90 Days