# douyin

[![CI](https://github.com/tamnd/douyin-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/tamnd/douyin-cli/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/tamnd/douyin-cli)](https://github.com/tamnd/douyin-cli/releases/latest)
[![Go Reference](https://pkg.go.dev/badge/github.com/tamnd/douyin-cli.svg)](https://pkg.go.dev/github.com/tamnd/douyin-cli)
[![Go Report Card](https://goreportcard.com/badge/github.com/tamnd/douyin-cli)](https://goreportcard.com/report/github.com/tamnd/douyin-cli)
[![License](https://img.shields.io/github/license/tamnd/douyin-cli)](./LICENSE)

A command line for Douyin (抖音). `douyin` reads public Douyin data and prints
clean, pipeable records. One pure-Go binary, no API key, no login.

[Install](#install) • [Commands](#commands) • [Usage](#usage) • [Anti-bot](#anti-bot) • [Serve](#serve-it) • [Docs](https://douyin-cli.tamnd.com)

![douyin reading the hot-search board into a table and piping it through jq](docs/static/demo.gif)

`douyin` reads the same public web surface a logged-out browser sees: the open
iesdouyin hot-search billboards, and the `www.douyin.com/aweme/v1/web/*`
endpoints the page's own JavaScript calls, signed the way the web client signs
them. It returns records as a table, JSON, JSONL, CSV, TSV, or URLs, and serves
the same read operations over HTTP and MCP. Output is a table at a terminal and
JSONL when piped, so `douyin hot | jq` works with no flags.

`douyin` is an independent tool. It is not affiliated with, authorized, or
endorsed by ByteDance or Douyin.

## Install

```bash
go install github.com/tamnd/douyin-cli/cmd/douyin@latest
```

Or with Homebrew:

```bash
brew install tamnd/tap/douyin
```

Or grab a prebuilt binary from the [releases](https://github.com/tamnd/douyin-cli/releases),
or run the container image:

```bash
docker run --rm ghcr.io/tamnd/douyin:latest hot
```

Shell completion is built in: `douyin completion bash|zsh|fish|powershell`.

## Commands

Read operations work from the CLI, over HTTP (`douyin serve`), and over MCP
(`douyin mcp`).

| Command | Description |
|---|---|
| `hot` | Hot-search billboard, in rank order (`--tab` realtime\|video\|music\|star) |
| `video <url\|id>` | One video record |
| `user <sec_uid\|url>` | Profile record for a sec_uid |
| `posts <sec_uid\|url>` | A user's public videos |
| `comments <url\|id>` | Comments under a video |
| `search <query>` | Mixed search hits (videos and users) |

| Command | Description |
|---|---|
| `serve` | Serve the read operations over HTTP (NDJSON) |
| `mcp` | Run as an MCP server over stdio |
| `raw <url>` | Fetch a URL and print its body untouched |
| `version` | Print version information |

## Usage

```bash
douyin hot                              # realtime hot-search board, in rank order
douyin hot --tab video -n 10            # top 10 hot videos
douyin hot --fields rank,word,hot_value # pick the columns
douyin video 7400000000000000000        # one video record
douyin search 美食 -n 20                 # search hits
douyin hot -o json                      # JSON output
douyin hot | jq                         # JSONL piped to jq
```

Output is a table when you are at a terminal and JSONL when you pipe, so the
last line works with no flags. Every command takes the same global flags
(`-o/--output`, `--fields`, `-n/--limit`, `--rate`, `--timeout`, and more); see
the [CLI reference](https://douyin-cli.tamnd.com/reference/cli/) for the full
surface.

## Anti-bot

Douyin serves some surfaces openly and walls the rest behind its web anti-bot
system. The tool is honest about this: it recognises each block signal and exits
cleanly rather than emitting a challenge page or faking data.

- **Open** anywhere, no key: `hot` (the hot-search billboard).
- **Walled** behind the web anti-bot challenge: `video`, `user`, `posts`,
  `comments`, and `search`. These call the signed `www.douyin.com/aweme/v1/web/*`
  API with a reimplemented `a_bogus` signature and a fresh `msToken`. From a
  datacenter or non-China IP, Douyin answers the signed call with an empty body
  or an `__ac` challenge shell; the command detects this and exits with code 4
  (needs a residential session). The signing and parsers are correct and serve
  from a China IP or a warm browser session. The wall is at the IP and session
  layer, so no key the client could send opens it.

## Serve it

The read operations are also an HTTP API and an MCP server, backed by the same
code as the CLI.

```bash
douyin serve                 # HTTP on :8080, NDJSON; /healthz and /v1/openapi.json
douyin mcp                   # MCP server over stdio
```

## Docs

Full documentation lives at [douyin-cli.tamnd.com](https://douyin-cli.tamnd.com).

## License

Apache-2.0. See [LICENSE](./LICENSE). `douyin` is an independent tool and is not
affiliated with ByteDance or Douyin.
