Douyin API in Python: videos, profiles and comments (2026)
Douyin's official Open Platform serves the data of users who authorise your app, so this guide calls five okdata endpoints from plain requests instead: video search, video detail, account profile, account videos and video comments. The worked example is 6 calls and costs $0.03; a failed call is not charged.
Updated
Is there an official Douyin API for this? The Douyin Open Platform is built around user authorisation. Its profile call, 获取用户公开信息, takes an access_token and an open_id that your app only receives after that user logs in and grants the user_info scope. The platform also lists a keyword video search (scope aweme.dy.video_search), but it sits behind a separate permission your app has to be granted. If you build for accounts that log in to your app, or your app holds that permission, the official route is the right one. More on the split in what the Douyin Open Platform covers.
This guide is for the other case: you want public Douyin data in a Python script without an app review, OAuth flow, cookie or signature code. You end with a small client you can reuse: one helper that handles errors and retries, a pager with a page cap, five endpoints and a JSON report.
Step by step
Step 1: set up
You need Python 3.9 or newer, requests and an API key in an environment variable. There is no SDK to install.
pip install requests
export OKDATA_API_KEY=okd_...
Every call is a GET to https://api.okdata.online plus the endpoint path, with the key as a bearer token. A success has "code": "ok", the data under data, and for lists a page object. An error has a code, a message, a request_id and a retry hint: yes means try again later (wait retry_after_seconds when it is set), anything else means fix the request or skip it.
Step 2: search videos
Douyin video search takes query and sort: relevance (the default), latest or most_liked. One call returns about 20 videos for $0.02. Each has id, the caption in text, author (with author.id, the sec_uid), metrics with likes, comments, shares and favorites, published_at and hashtags. Image posts come with type: "image" and their images.
curl -G "https://api.okdata.online/v1/douyin/videos/search" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'query=露营装备' \
--data-urlencode 'sort=most_liked'
Step 3: one video by ID or link
Douyin video detail takes id or url, and a v.douyin.com share link works as url. It costs $0.002. Use it to refresh the counts of a video you already know, or when all you have is a link someone pasted. With view=full you also get ext.duration_ms and the music title in ext.music.
curl -G "https://api.okdata.online/v1/douyin/videos/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=7438215906473820455' \
--data-urlencode 'view=full'
Step 4: the author's profile
Douyin account profile takes the sec_uid as id, or a profile or share link as url, for $0.002. You get followers, following, posts (videos published), likes (total likes received), bio and verified. username is the Douyin number people see in the app.
curl -G "https://api.okdata.online/v1/douyin/users/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=MS4wLjABAAAAk3Vb9QpZr2LxWc7nYfTe1HdJ6sGuA0mKiOyPzR4tE8w'
Step 5: the author's videos
Douyin account videos needs the sec_uid as id and returns about 20 videos per call, newest first, for $0.002. Page with cursor like every list.
Step 6: comments under a video
Douyin video comments needs the video id and returns about 20 top-level comments per call for $0.002, in Douyin's hot order (sort=hot is the only value). Each comment has text, author, likes, replies, published_at and ip_location.
curl -G "https://api.okdata.online/v1/douyin/videos/comments/list" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'id=7438215906473820455'
Step 7: the whole script
The script runs the worked example: search, take the most-liked video, fetch its detail, its author's profile and latest videos, two pages of comments, and write douyin_report.json. call sends one Idempotency-Key per logical call and reuses it on retries, so a retried request is never charged twice.
import json
import os
import time
import uuid
import requests
API = "https://api.okdata.online"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['OKDATA_API_KEY']}"
COMMENT_PAGES = 2 # each page is one call
class ApiError(Exception):
def __init__(self, body):
super().__init__(f"{body['code']}: {body['message']} (request {body['request_id']})")
self.code = body["code"]
def call(path, **params):
key = str(uuid.uuid4()) # same key on every retry: a replay is not charged again
for attempt in range(3):
body = SESSION.get(API + path, params=params, headers={"Idempotency-Key": key}, timeout=60).json()
if body["code"] == "ok":
return body
if body.get("retry") == "yes" and attempt < 2:
time.sleep(body.get("retry_after_seconds") or 5)
continue
raise ApiError(body)
def pages(path, max_pages, **params):
"""Yield items page by page; stop at max_pages or when has_more is false."""
cursor = None
for _ in range(max_pages):
body = call(path, **params, **({"cursor": cursor} if cursor else {}))
yield from body["data"]["items"]
if not body["page"]["has_more"]:
return
cursor = body["page"]["next_cursor"]
found = call("/v1/douyin/videos/search", query="露营装备", sort="most_liked")["data"]["items"]
if not found:
raise SystemExit("no videos for that query")
top = max(found, key=lambda v: v["metrics"]["likes"] or 0)
video = call("/v1/douyin/videos/get", id=top["id"], view="full")["data"]["item"]
author = call("/v1/douyin/users/get", id=video["author"]["id"])["data"]["item"]
latest = call("/v1/douyin/users/videos/list", id=author["id"])["data"]["items"]
comments = list(pages("/v1/douyin/videos/comments/list", COMMENT_PAGES, id=video["id"]))
report = {
"video": {"id": video["id"], "text": video["text"], "likes": video["metrics"]["likes"],
"comments": video["metrics"]["comments"], "music": video.get("ext", {}).get("music")},
"author": {"sec_uid": author["id"], "name": author["name"], "followers": author["followers"],
"videos": author["posts"], "likes": author["likes"]},
"latest_videos": [{"id": v["id"], "text": v["text"], "published_at": v["published_at"],
"likes": v["metrics"]["likes"]} for v in latest],
"comments": [{"text": c["text"], "likes": c["likes"], "replies": c["replies"],
"ip_location": c["ip_location"]} for c in comments],
}
with open("douyin_report.json", "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
To go deeper, raise COMMENT_PAGES or wrap the account videos call in pages too. Ten pages of comments under each of 20 videos is 200 calls, $0.40.
Good to know
- No play counts. Douyin does not show view counts publicly, so
metrics.viewsis null on every video andmeta.unavailablesaysplatform.titleis null too: a Douyin caption is intext. - Three kinds of account ID. The sec_uid (starts with
MS4wLjABAAAA) is what the account endpoints take. The Douyin number isusername. The numeric uid appears asext.uidwithview=full. See how to get a sec_uid. - Links work as input. Video detail and account profile take share links directly; converting a share link to a video ID explains the formats.
- Deleted is free. A removed video or a banned account returns
not_found, which is not charged. The script raisesApiErrorwithcode == "not_found"; catch it and move on in a batch job. - Replies are counted, not listed. Comments carry
replies, the number of replies under them, but not the reply text. - Spend is bounded by your caps. Each page is a call, so
max_pagesinpagesis also a spending cap. Errors cost nothing.
For comments across many videos at once, the step-by-step Douyin comments export goes from one search to a CSV. To read the comments for opinions, see turning Douyin comments into a sentiment summary.
What the example costs
One search for 露营装备 (camping gear), the most-liked video's detail, its author's profile and latest videos, and two pages of comments under that video
| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| Douyin video search | 1 | $0.02 | $0.02 |
| Douyin video detail | 1 | $0.002 | $0.002 |
| Douyin account profile | 1 | $0.002 | $0.002 |
| Douyin account videos | 1 | $0.002 | $0.002 |
| Douyin video comments | 2 | $0.002 | $0.004 |
| Total | 6 | $0.03 |
Prices in force when this page was built. A failed call costs nothing.
In your AI (MCP)
Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.
SaySearch Douyin for 露营装备 (camping gear), open the most-liked video, look up its author, list the author's latest videos and read the first two pages of comments under that video. Summarise what the comments ask about.
What the agent sends to the call tool first
{
"endpoint": "douyin.videos.search",
"params": {
"query": "露营装备"
},
"idempotency_key": "<a new UUID>"
}