
Douyin Search Keyword
- 5 installs
- 401 repo stars
- Updated July 28, 2026
- aiskillstore/marketplace
Douyin Search Keyword is an agent skill that runs validated Douyin keyword searches and returns up to 60 ranked results as JSON or markdown.
About
Douyin Search Keyword is an agent skill wired to a Douyin search integration that validates and sanitizes a query, applies sort and time filters, and returns a bounded set of search results for solo builders studying short-video trends in China. Indie creators and marketers shipping cross-border or Douyin-native campaigns install it when they need structured discovery instead of manual scrolling. The workflow enforces keyword length and character rules, normalizes unsafe input, and defaults invalid sort, time, limit, or output options with clear errors. Output formats suit piping into spreadsheets, briefs, or agent follow-ups for scripting and positioning. It matters for Prism’s Grow journey because it turns a platform-specific search chore into a repeatable research step before you commit creative or ad spend.
- Validates keywords (2–50 characters, blocks URLs and unsafe characters)
- Sanitizes input to Chinese, Latin, digits, and basic punctuation
- Sort modes 0, 1, and 2 plus publish-window filters 0, 1, 7, and 180 days
- Result limit 1–60 with JSON or markdown output
- Calls the guaikei.com-backed search API via HTTPS from a small Node module
Douyin Search Keyword by the numbers
- 5 all-time installs (skills.sh)
- Ranked #1,583 of 1,881 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aiskillstore/marketplace --skill douyin-search-keywordAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 401 |
| Last updated | July 28, 2026 |
| Repository | aiskillstore/marketplace ↗ |
What it does
Search Douyin by keyword to pull ranked video results for trend and competitor content research.
Who is it for?
Best when you're researching Douyin trends, hooks, and competitor videos before writing scripts or scheduling posts.
Skip if: Skip if you only need Western short-video platforms, cannot use third-party Douyin API gateways, or want deep analytics without search listings.
When should I use this skill?
You need Douyin search results for a sanitized keyword with optional sort, time range, limit, and JSON or markdown output.
What you get
You get a sanitized query executed with explicit sort, time window, and limit, plus machine-readable or markdown listings you can drop into content plans.
- Validated and sanitized search keyword payload
- Douyin search result set (JSON or markdown) up to the chosen limit
By the numbers
- Keyword length enforced between 2 and 50 characters
- Search limit range 1–60 results per request
- Four publish-window filters: 0, 1, 7, and 180 days
Files
抖音搜索关键词技能 (Douyin Search Keyword)
1. 技能概述
1.1 核心定位
抖音公开内容智能搜索,精准检索视频/图文/用户数据,支持多维度排序与时间筛选,输出结构化JSON/Markdown,助力短视频营销、竞品分析与热点追踪。
1.2 核心能力
- 🔍 抖音热门搜索:精准检索视频/图文/用户数据
- 🎯 多维度排序:按点赞数、最新发布智能排序
- 📅 时间筛选:支持1天/7天/半年内数据精准筛选
- 📊 互动数据:提取点赞、评论、收藏、分享等核心指标
- 🛠️ 智能纠错:自动清洗关键词,提升检索准确率
- 📦 多格式输出:JSON(程序处理)/Markdown(人工阅读)双格式
1.3 适用场景
| 场景 | 用户痛点 | 技能如何解决 |
|---|---|---|
| 短视频营销 | 缺乏爆款视频创意灵感 | 一键获取“抖音热门”视频数据,分析热门趋势 |
| 竞品分析 | 难以追踪竞品内容策略 | 精准搜索竞品账号视频,分析互动数据 |
| 热点追踪 | 错过热点话题黄金期 | 实时搜索“抖音热门”话题,掌握最新动态 |
- 链接提取:直接获取视频下载地址和图文原始链接
1.4 技能特性
- 实时搜索:获取最新的抖音公开内容
- 参数灵活:支持排序、时间、数量、格式自定义
- 安全可靠:仅采集公开数据,符合数据采集合规要求
- 易于集成:支持 OpenClaw 环境和直接命令行调用
- 多维度数据:返回视频、图文等多种类型内容
- 详细信息:包含标题、发布人、互动数据等完整信息
1.5 技术原理
该技能通过调用抖音搜索API,实现关键词搜索功能。具体流程如下:
1. 接收用户输入的搜索关键词 2. 清洗关键词,移除特殊符号 3. 验证关键词格式 4. 调用API创建搜索任务 5. 轮询获取搜索结果 6. 格式化输出结果 7. 保存结果到本地文件
2. 快速调用指南
2.1 前置条件
- 安装Node.js 16+环境
- 配置环境变量
GUAIKEI_API_TOKEN(默认TOKEN仅用于体验,私有TOKEN需申请)
2.2 基础语法
# 语法:node scripts/search.js [关键词] [选项]2.3 选项说明
| 选项 | 类型 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
| --keyword | string | 2-50字符(无特殊符号) | 无 | 搜索关键词(必传) |
| --sort | number | 0/1/2 | 0 | 排序方式(0 - 综合 / 1 - 最多点赞 / 2 - 最新) |
| --time | number | 0/1/7/180 | 0 | 发布时间范围(0 - 全部 / 1-1 天 / 7-7 天 / 180 - 半年) |
| --limit | number | 10-60 | 10 | 返回结果数量 |
| --output | string | json/markdown | json | 输出格式 |
| --help/-h | - | - | - | 显示帮助信息 |
2.4 典型示例
# 示例1:基础搜索(JSON格式)
node scripts/search.js AI
# 示例2:带空格的关键词
node scripts/search.js "AI 教程"
# 示例3:自定义排序(最多点赞)
node scripts/search.js AI --sort 1
# 示例4:自定义发布时间(半年)
node scripts/search.js "AI 模型" --time 180
# 示例5:自定义返回结果数量(20条)
node scripts/search.js AI --limit 20
# 示例6:自定义输出格式(Markdown)
node scripts/search.js "AI 教程" --output markdown
# 示例7:复杂搜索(最新+近半年+20条结果+JSON格式)
node scripts/search.js --keyword "AI 教程" --sort 2 --time 180 --limit 203. 输出数据规范
3.1 JSON格式(默认)
{
"status": "success",
"keyword": "AI 教程",
"message": "搜索任务完成",
"sort": 0,
"time": 0,
"limit": 20,
"output_format": "json",
"total": 18,
"timestamp": "2026/3/29 09:05:51",
"results": [
{
"aweme_id": "7622261059679800627",
"desc": "#刘慈欣称AI不可能完全代替人类作者 科幻作家谈#AI快速发展对科幻产业影响几何",
"create_time": 1774695958,
"author_uid": "98524606968",
"author_nickname": "央视财经",
"author_avatar": "https://...",
"author_sec_uid": "MS4wLjABAAAAt6AsGhjrHeoxZNkceYg2J0FWvrWKzEaTAvF44-sPYco",
"comment_count": 4,
"digg_count": 356,
"share_count": 6,
"collect_count": 17,
"share_url": "https://www.iesdouyin.com/share/video/...",
"dynamic_cover": ["https://...", "https://..."],
"play_addr": "https://...",
"play_uri": "v0200fg10000d73r95fog65tmhj30190",
"music_id": "7622261065841150756",
"music_title": "@央视财经创作的原声",
"music_author": "央视财经",
"tags": [
"刘慈欣称AI不可能完全代替人类作者",
"AI快速发展对科幻产业影响几何"
],
"url": "https://www.douyin.com/video/xxx",
"author_url": "https://www.douyin.com/user/xxx",
"create_time_str": "2026/3/28 19:05:58"
}
]
}3.2 Markdown格式(人工阅读)
以结构化列表形式展示搜索结果,非常适合内容创作者快速浏览和整理灵感素材。
## **抖音综合搜索结果**: AI 教程
**1 .** 刘慈欣称AI不可能完全代替人类作者 科幻作家谈#AI快速发展对科幻产业影响几何
**发布人**: 央视财经
**发布时间**: 2026/3/28 19:05:58
**链接**: https://www.douyin.com/video/xxx
**封面**: https://...
**视频**: https://...
**点赞**: 364 **评论**: 4 **收藏**: 18 **分享**: 7
---
**共 20 条结果**4. 注意事项
4.1 合规要求
- 仅用于抖音公开数据采集,禁止爬取私密 / 违规内容
- 符合 OpenClaw 安全规范与数据采集合规要求
4.2 风控提示
- 默认 TOKEN 有调用频率限制,生产环境建议使用私有 TOKEN
- CDN 链接(视频 / 图片)为临时链接,建议及时下载留存
- 关键词包含违规内容时,技能会直接拦截并提示
/**
* 抖音搜索模块
*/
const https = require("https");
const querystring = require("querystring");
const BASE_URL = "www.guaikei.com";
const utils = require("./utils");
/**
* 检查搜索关键词是否符合要求
*/
function notIdealFormat(keyword) {
keyword = keyword.trim();
if (keyword.length < 2) {
utils.printError(`搜索关键词长度不能小于 2 个字符`);
return false;
}
if (keyword.length > 50) {
utils.printError(`搜索关键词长度不能超过 50 个字符`);
return false;
}
if (/[<>\"'&]/g.test(keyword)) {
utils.printError(`搜索关键词包含特殊字符, 请输入普通关键词, 例如: 新媒体`);
return false;
}
if (keyword.includes("http")) {
utils.printError(
`搜索关键词包含 http 链接, 请输入普通关键词, 例如: 新媒体`,
);
return false;
}
return true;
}
/**
* 清洗搜索关键词
*/
function sanitizeKeyword(keyword) {
let trimmed = keyword.trim();
return trimmed.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s.,!?# ,。!?]/g, "");
}
/**
* 格式化搜索选项, 并检查是否有效
*/
function optionFormat(sort, time, limit, output) {
sort = sort || 0;
time = time || 0;
limit = limit || 10;
output = output || "json";
if (sort !== 0 && sort !== 1 && sort !== 2) {
utils.printError(`排序依据 ${sort} 无效, 请使用 0, 1, 2。 默认值为 0`);
sort = 0;
}
if (time !== 0 && time !== 1 && time !== 7 && time !== 180) {
utils.printError(`发布时间 ${time} 无效, 请使用 0, 1, 7, 180。 默认值为 0`);
time = 0;
}
if (limit < 1 || limit > 60) {
utils.printError(`搜索数量 ${limit} 无效, 请使用 1-60。 默认值为 10`);
limit = 10;
}
if (output !== "json" && output !== "markdown") {
utils.printError(
`输出格式 ${output} 无效, 请使用 json, markdown。 默认值为 json`,
);
output = "json";
}
return [sort, time, limit, output];
}
function formatMessage(keyword, result) {
let message = `**抖音综合搜索结果**: ${keyword}\n`;
message += "-".repeat(35) + "\n\n";
for (let i = 0; i < result.length; i++) {
const item = result[i];
message += `**${i + 1} .** ${item.desc || "[无标题]"}\n`;
message += `**发布人**: ${item.author_nickname || "[未知]"}\n`;
message += `**发布时间**: ${item.create_time_str || "[未知]"}\n`;
message += `**链接**: ${item.url || "[未知]"}\n`;
if (item.dynamic_cover && item.dynamic_cover.length > 0) {
message += `**封面**: ${item.dynamic_cover[0] || ""}\n`;
}
if (item.play_addr) {
message += `**视频**: ${item.play_addr}\n`;
}
if (item.images && item.images.length > 0) {
message += `**图文**: ${item.images.slice(0, 3).join(", ")}...\n`;
}
message += `**点赞**: ${item.digg_count || 0}\t`;
message += `**评论**: ${item.comment_count || 0}\t`;
message += `**收藏**: ${item.collect_count || 0}\t`;
message += `**分享**: ${item.share_count || 0}\n`;
message += "\n";
}
message += "-".repeat(35) + "\n";
message += `**共 ${result.length} 条结果**\n`;
return message;
}
async function createWithRetry(token, keyword, sort, time, limit) {
let lastError = null;
const retryIntervals = [1000, 2000, 3000];
for (let attempt = 0; attempt < 3; attempt++) {
try {
const timeoutPromise = new Promise((_, reject) =>
setTimeout(
() => reject(new Error(`创建任务超时 (${attempt + 1}/3)`)),
10000,
),
);
const task = await Promise.race([
createSearchTask(token, keyword, sort, time, limit),
timeoutPromise,
]);
if (task.errcode === 0) return task;
throw new Error(`创建任务失败, 错误信息: ${JSON.stringify(task.errmsg)}`);
} catch (error) {
lastError = error;
utils.printInfo(
`【创建任务重试】 ${attempt + 1}/3 次 - ${error.message}`,
);
if (attempt < 2) {
await new Promise((resolve) =>
setTimeout(resolve, retryIntervals[attempt]),
);
}
}
}
throw lastError || new Error("创建搜索任务失败, 3次重试均失败");
}
/**
* 创建抖音搜索任务
* @param {string} token - API令牌
* @param {string} keyword - 搜索关键词
* @param {number} sort - 排序依据, 0: 综合排序, 1: 最多点赞, 2: 最新发布
* @param {number} time - 发布时间, 0: 全部, 1: 一天内, 7: 七天内, 180: 半年内
* @param {number} limit - 搜索数量, 1-60
* @returns {Promise<Object>} 搜索任务状态
* @throws {Error} API调用失败时抛出错误
*/
async function createSearchTask(token, keyword, sort, time, limit) {
return new Promise((resolve, reject) => {
const url = "/api/douyin/general-search/keyword";
const params = { _: Date.now(), token: token };
const data = JSON.stringify({
keyword,
sort_type: sort,
publish_time: time,
limit: limit,
});
const options = {
hostname: BASE_URL,
path: url + "?" + querystring.stringify(params),
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(data),
},
timeout: 20000,
};
const req = https.request(options, (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
if (res.statusCode === 200) {
try {
const json = JSON.parse(body);
if (json.errcode === 0) {
resolve(json);
} else {
reject(new Error(`请求错误信息: ${json.errmsg}`));
return;
}
} catch (error) {
reject(new Error(`解析响应失败: ${error.message}`));
return;
}
} else if (
res.statusCode === 401 ||
res.statusCode === 407 ||
res.statusCode === 403 ||
res.statusCode === 410 ||
res.statusCode === 408
) {
reject(new Error(`GUAIKEI_API_TOKEN 无效, 请检查环境变量`));
} else {
reject(new Error(`请求失败, 状态码: ${res.statusCode}`));
}
});
});
req.on("error", (err) => {
reject(err);
});
req.write(data);
req.end();
});
}
async function searchWithRetry(token, keyword, sort, time, limit) {
let lastError = null;
const maxAttempts = 60;
const retryInterval = 2000;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`查询结果超时 (${attempt + 1}/${maxAttempts})`));
}, 5000);
});
const data = await Promise.race([
getSearchTask(token, keyword, sort, time, limit),
timeoutPromise,
]);
if (Array.isArray(data) && data.length > 0) return data;
throw new Error(`第 ${attempt + 1} 次查询无结果`);
} catch (error) {
lastError = error;
utils.printInfo(
`【查询结果重试】尝试 ${attempt + 1} / ${maxAttempts} - ${error.message}`,
);
await new Promise((resolve) => setTimeout(resolve, retryInterval));
}
}
throw lastError || new Error(`查询搜索结果失败, ${maxAttempts}次重试均失败`);
}
/**
* 获取抖音搜索任务结果
* @param {string} token - API令牌
* @param {string} keyword - 搜索关键词
* @param {number} sort - 排序依据, 0: 综合排序, 1: 最多点赞, 2: 最新发布
* @param {number} time - 发布时间, 0: 全部, 1: 一天内, 7: 七天内, 180: 半年内
* @param {number} limit - 搜索数量, 1-60
* @returns {Promise<Array>} 搜索结果数组
* @throws {Error} API调用失败时抛出错误
*/
async function getSearchTask(token, keyword, sort, time, limit) {
return new Promise((resolve, reject) => {
const url = "/api/douyin/general-search/info";
const params = {
_: Date.now(),
token: token,
keyword: keyword,
sort_type: sort,
publish_time: time,
limit: limit,
};
const options = {
hostname: BASE_URL,
path: url + "?" + querystring.stringify(params),
method: "GET",
headers: {
"Content-Type": "application/json",
},
timeout: 20000,
};
const req = https.request(options, (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
if (res.statusCode === 200) {
try {
const json = JSON.parse(body);
if (json.errcode === 0) {
for (let i = 0; i < json.data.length; i++) {
const item = json.data[i];
if (item.author_sec_uid) {
json.data[i].author_url =
"https://www.douyin.com/user/" + item.author_sec_uid;
}
if (item.create_time && !item.create_time_str) {
json.data[i].create_time_str = new Date(
item.create_time * 1000,
).toLocaleString();
}
}
resolve(json.data);
} else {
reject(new Error(`请求错误信息: ${json.errmsg}`));
return;
}
} catch (error) {
reject(new Error(`解析响应失败: ${error.message}`));
return;
}
} else if (
res.statusCode === 401 ||
res.statusCode === 407 ||
res.statusCode === 403 ||
res.statusCode === 410 ||
res.statusCode === 408
) {
reject(new Error(`GUAIKEI_API_TOKEN 无效, 请检查环境变量`));
} else {
reject(new Error(`请求失败, 状态码: ${res.statusCode}`));
}
});
});
req.on("error", (err) => {
reject(err);
});
req.end();
});
}
module.exports = {
createWithRetry,
formatMessage,
optionFormat,
notIdealFormat,
sanitizeKeyword,
searchWithRetry,
};
/**
* TOKEN管理模块
*/
const utils = require("./utils");
function apiKey(token) {
let isDefault = false;
if (token == undefined) {
isDefault = true;
} else if (token == "") {
isDefault = true;
} else if (typeof token != "string") {
isDefault = true;
} else if (token.length != 32) {
isDefault = true;
}
if (isDefault) {
utils.printError(
"未正确设置 GUAIKEI_API_TOKEN 环境变量, 将使用默认值, 可能影响搜索效率或搜索频率受限, 建议升级为私有TOKEN以获得更好的搜索体验",
);
return "e10adc3949ba59abbe56e057f20f883e";
} else {
return token;
}
}
module.exports = {
apiKey,
};
/**
* 通用工具函数模块
*/
function printBanner() {
console.log("╔════════════════════════════════════════════╗");
console.log("║ ║");
console.log("║ 🎬 抖音搜索关键词 ║");
console.log("║ ║");
console.log("╚════════════════════════════════════════════╝");
console.log("");
}
function printLog(level, message) {
const colorMap = {
INFO: "\x1b[34m",
SUCCESS: "\x1b[32m",
WARN: "\x1b[33m",
ERROR: "\x1b[31m",
};
console.log(
`${colorMap[level] || ""}[${new Date().toLocaleString()}] [${level}] ${message}\x1b[0m`,
);
}
module.exports = {
printBanner,
printInfo: (msg) => printLog("INFO", msg),
printSuccess: (msg) => printLog("SUCCESS", msg),
printError: (msg) => printLog("ERROR", msg),
printWarn: (msg) => printLog("WARN", msg),
};
{
"name": "douyin-search-keyword",
"version": "1.1.1",
"description": "抖音公开内容智能搜索,精准检索视频/图文/用户数据,支持多维度排序与时间筛选,输出结构化JSON/Markdown,助力短视频营销、竞品分析与热点追踪",
"main": "scripts/search.js",
"scripts": {
"search": "node scripts/search.js"
},
"keywords": [
"douyin",
"search"
],
"author": "um-why",
"license": "MIT",
"openclaw": {
"require": {
"bins": [
"node"
],
"env": [
"GUAIKEI_API_TOKEN"
]
}
}
}Skill Name: 抖音搜索
Description
抖音公开内容智能搜索,精准检索视频/图文/用户数据,支持多维度排序与时间筛选,输出结构化JSON/Markdown,助力短视频营销、竞品分析与热点追踪。
Change Log
1.1.0 - 2026-03-31 重大升级:结构化输出、多参数排序与筛选能力增强
- 增加多维度排序(综合/最多点赞/最新发布)和发布时间筛选(1天/7天/半年),结果更精准。
- 支持自定义返回数量(1~60条),输出格式新增 Markdown,与 JSON 并行。
- 完全结构化 JSON 输出,字段覆盖互动数据、标签等,适配 OpenClaw 生态。
- 内置参数合法性校验、敏感词过滤及安全保护,保证调用稳定合规。
- 增强任务轮询&超时重试机制,提升检索成功率和稳定性。
- 技能文档全面更新,提供详细参数表、示例与应用场景说明。
1.0.2 - 2026-03-24
- 技能重命名为“douyin-search-keyword”。
- 在SKILL.md中添加了openclaw元数据、使用帮助、许可证、标签和示例,以实现更好的集成与文档化。
- 移除了两个本地文件(.env 和 scripts/last-search.json),以优化代码结构并提升安全性。
- 文档现已更加简洁且以用户为中心,重点在于提供清晰的使用说明和数据字段解释。
- 突出技能特性、合规要点及技术流程。
1.0.1 - 2026-3-20
- 更新技能描述,强调支持多维度抖音公开内容检索(视频、图文、用户等)
- 扩展文档,新增适用/不适用场景说明
- 新增环境和token配置的详细步骤与平台限制说明
- 明确数据字段及样例,补充表格形式字段解释
- 强调合规、风控及临时链接有效期注意事项
- 版本号由 1.0.0 升级为 1.0.1,内容结构更清晰,便于理解与使用
1.0.0 - 2026-03-18
- douyin-search-general 技能首次发布。
- 提供抖音通用搜索接口,支持视频、图文、用户及社交数据搜索。
- 返回详细信息,包括内容描述、作者、发布时间、点赞数、评论数、分享数、封面图片及视频链接。
- 需要Node.js和有效的GUAIKEI_API_TOKEN环境变量才能运行。
- 输出包含可直接复制的URL,并附有示例响应格式说明。
#!/usr/bin/env node
const utils = require("../lib/utils");
const key = require("../lib/key");
const douyin = require("../lib/douyin");
const fs = require("fs");
const path = require("path");
/**
* 打印帮助信息
*/
function printHelp() {
console.log(`
用法: node scripts/search.js <关键词> [选项]
选项:
--keyword \t<关键词> \t搜索关键词
--sort \t<排序> \t排序依据, 0: 综合排序(默认), 1: 最多点赞, 2: 最新发布
--time \t<时间> \t发布时间, 0: 全部(默认), 1: 一天内, 7: 七天内, 180: 半年内
--limit \t<数量> \t搜索数量 (默认 10, 最大 60)
--output \t<格式> \t输出格式, json, markdown (默认 json)
--help \t显示帮助信息
示例1: node scripts/search.js AI
示例2: node scripts/search.js "AI 模型"
示例3: node scripts/search.js --keyword AI --sort 0 --time 0 --limit 10 --output json
示例4: node scripts/search.js --keyword "AI 模型" --sort 1 --time 180 --limit 20 --output markdown
注意:
- 关键词建议 2-50 个汉字,避免特殊符号
- 请确保环境变量 GUAIKEI_API_TOKEN 已配置
- 所有参数都会自动清洗和验证
`);
}
process.on("SIGTERM", () => {
utils.printWarn("OpenClaw 终止任务, 清理临时文件...");
const outputPath = path.join(__dirname, "last-search.json");
if (fs.existsSync(outputPath)) {
fs.unlinkSync(outputPath);
}
process.exit(0);
});
async function main() {
const startTime = Date.now();
const args = process.argv.slice(2);
if (args.length === 0) {
printHelp();
return;
}
let keyword = "",
sort = 0,
time = 0,
limit = 10,
output = "json";
args.forEach((arg, index) => {
if (arg === "--keyword") {
keyword = args[index + 1] || "";
} else if (arg === "--sort") {
sort = args[index + 1] || 0;
sort = Number(sort);
} else if (arg === "--time") {
time = args[index + 1] || 0;
time = Number(time);
} else if (arg === "--limit") {
limit = args[index + 1] || 10;
limit = Number(limit);
} else if (arg === "--output") {
output = args[index + 1] || "json";
} else if (arg === "--help" || arg === "-h") {
printHelp();
return;
} else if (arg.startsWith("--") === false && keyword === "") {
keyword = arg;
}
});
if (keyword === "") {
utils.printError(`未提供关键词`);
printHelp();
return;
}
utils.printBanner();
utils.printInfo(`原始关键词: ${keyword}`);
let isRight = douyin.notIdealFormat(keyword);
if (!isRight) {
return;
}
keyword = douyin.sanitizeKeyword(keyword);
utils.printInfo(`清洗后关键词: ${keyword}`);
[sort, time, limit, output] = douyin.optionFormat(sort, time, limit, output);
utils.printInfo(
`排序: ${sort}, 时间: ${time}, 数量: ${limit}, 输出格式: ${output}`,
);
// 幂等性校验: 同一关键词+参数 2 分钟内不重复执行
const taskId = `${keyword}_${sort}_${time}_${limit}`;
const taskLockFile = path.join(__dirname, `.lock_${taskId}`);
if (fs.existsSync(taskLockFile)) {
const lockTime = fs.statSync(taskLockFile).mtimeMs;
if (Date.now() - lockTime < 2 * 60 * 1000) {
utils.printError(`同一任务 2 分钟内执行, 避免重复请求 API`);
process.exit(1);
} else {
fs.unlinkSync(taskLockFile);
}
}
// 创建锁文件
fs.writeFileSync(taskLockFile, Date.now().toString(), { mode: 0o600 });
const token = key.apiKey(process.env.GUAIKEI_API_TOKEN);
let searchTask = null;
try {
const status = await douyin.createWithRetry(
token,
keyword,
sort,
time,
limit,
);
if (status.errcode !== 0) {
throw new Error(
`搜索任务创建失败时, 遇到未知错误, 请反馈给开发者 ${status} - ${Date.now()}`,
);
}
utils.printSuccess(`搜索任务创建成功, 正在搜索中...`);
searchTask = await douyin.searchWithRetry(
token,
keyword,
sort,
time,
limit,
);
} catch (error) {
const errorOutput = {
status: "error",
keyword: keyword,
message: error.message,
error_code: error.code || "UNKNOWN",
sort: sort,
time: time,
limit: limit,
output_format: output,
timestamp: new Date().toLocaleString(),
results: [],
};
console.log(JSON.stringify(errorOutput, null, 2));
return;
} finally {
// 删除锁文件
if (fs.existsSync(taskLockFile)) {
fs.unlinkSync(taskLockFile);
}
}
if (!searchTask || !Array.isArray(searchTask) || searchTask.length === 0) {
utils.printError(`搜索任务没有返回结果, 请稍后重试或联系开发者`);
const emptyOutput = {
status: "empty",
keyword: keyword,
message: "没有找到匹配的视频或图文内容",
sort: sort,
time: time,
limit: limit,
output_format: output,
timestamp: new Date().toLocaleString(),
results: [],
};
console.log(JSON.stringify(emptyOutput, null, 2));
return;
}
// 输出搜索结果
const finalOutput = {
status: "success",
keyword: keyword,
message: "搜索任务完成",
sort: sort,
time: time,
limit: limit,
output_format: output,
total: searchTask.length,
timestamp: new Date().toLocaleString(),
openclaw_metadata: {
skill_version: "1.1.1",
runtime_version: process.versions.node,
execution_time: Date.now() - startTime,
},
results: searchTask,
};
if (output === "markdown") {
const message = douyin.formatMessage(keyword, searchTask);
utils.printInfo(message);
utils.printSuccess(`搜索任务完成, 共返回 ${searchTask.length} 条结果`);
} else {
console.log(JSON.stringify(finalOutput, null, 2));
utils.printSuccess(`搜索任务完成, 共返回 ${finalOutput.total} 条结果`);
}
// 保存搜索结果到文件
const outputPath = path.join(__dirname, "last-search.json");
fs.writeFileSync(outputPath, JSON.stringify(finalOutput, null, 2));
utils.printSuccess(` → 已保存到 ${outputPath}`);
}
main().catch((error) => {
utils.printError(error);
process.exit(1);
});
{
"schema_version": "2.0",
"meta": {
"generated_at": "2026-04-06T08:50:40.179Z",
"slug": "um-why-douyin-search-keyword",
"source_url": "https://github.com/um-why/douyin-search-openclaw/tree/master/",
"source_ref": "master",
"model": "claude",
"analysis_version": "3.0.0",
"source_type": "community",
"content_hash": "dcac0b462febbc04772b01ca932b80870e961e656966efec4e53af5ad6251b15",
"tree_hash": "ca17ee1d1903b9ceacb774c432408ea5d6fd1a69502f81eb7ae6183f04ec2603"
},
"skill": {
"name": "douyin-search-keyword",
"description": "抖音公开内容智能搜索,精准检索视频/图文/用户数据,支持多维度排序与时间筛选,输出结构化 JSON/Markdown,助力短视频营销、竞品分析与热点追踪。",
"summary": "Intelligent Douyin content search with multi-dimensional sorting, time filtering, and structured JSON/Markdown output for marketing and competitive analysis.",
"icon": "📦",
"version": "1.1.1",
"author": "um-why",
"license": "MIT",
"category": "data",
"tags": [
"douyin",
"search",
"social-media",
"data-mining",
"tiktok"
],
"supported_tools": [
"claude",
"codex",
"claude-code"
],
"risk_factors": [
"network",
"filesystem",
"env_access"
]
},
"security_audit": {
"risk_level": "low",
"is_blocked": false,
"safe_to_publish": true,
"summary": "Static analyzer produced 59 false positive shell execution alerts - these are JavaScript template literals, not command injection. Legitimate HTTPS API client for Douyin search. Minor risks: third-party API dependency on guaikei.com, hardcoded default token placeholder, and file lock mechanism. No malicious patterns detected after manual review.",
"risk_factor_evidence": [
{
"factor": "network",
"evidence": [
{
"file": "lib/douyin.js",
"line_start": 163,
"line_end": 163
},
{
"file": "lib/douyin.js",
"line_start": 261,
"line_end": 261
}
]
},
{
"factor": "filesystem",
"evidence": [
{
"file": "scripts/search.js",
"line_start": 112,
"line_end": 112
},
{
"file": "scripts/search.js",
"line_start": 205,
"line_end": 205
}
]
},
{
"factor": "env_access",
"evidence": [
{
"file": "scripts/search.js",
"line_start": 114,
"line_end": 114
}
]
}
],
"critical_findings": [],
"high_findings": [],
"medium_findings": [],
"low_findings": [
{
"title": "Hardcoded Default API Token",
"description": "Default token 'e10adc3949ba59abbe56e057f20f883e' (MD5 of '123456') is hardcoded as fallback in lib/key.js. While documented as placeholder, it could be accidentally used in production.",
"locations": [
{
"file": "lib/key.js",
"line_start": 20,
"line_end": 20
}
],
"confidence": 0.85,
"confidence_reasoning": "Clear evidence of hardcoded credential placeholder - common anti-pattern that could lead to accidental exposure"
},
{
"title": "Third-Party API Dependency",
"description": "All API requests route through guaikei.com third-party service. Users must trust this intermediary with their search queries and API tokens.",
"locations": [
{
"file": "lib/douyin.js",
"line_start": 6,
"line_end": 6
},
{
"file": "lib/douyin.js",
"line_start": 154,
"line_end": 154
}
],
"confidence": 0.9,
"confidence_reasoning": "Explicit hostname constant and request targets confirm dependency on external service"
},
{
"title": "Lock File Race Condition",
"description": "File-based locking mechanism uses check-then-create pattern which could have race conditions under high concurrency. Lock files use predictable names based on task ID.",
"locations": [
{
"file": "scripts/search.js",
"line_start": 101,
"line_end": 112
}
],
"confidence": 0.6,
"confidence_reasoning": "Pattern visible in code but impact is limited to local file system - no remote exploitation vector"
}
],
"dangerous_patterns": [],
"files_scanned": 8,
"total_lines": 1056,
"audit_model": "claude",
"audited_at": "2026-04-06T08:50:40.179Z"
},
"content": {
"user_title": "Search Douyin Content with Intelligent Filtering",
"value_statement": "Marketers and researchers struggle to extract structured data from Douyin's platform. This skill provides real-time search with customizable sorting, time filtering, and structured JSON/Markdown output for analysis workflows.",
"seo_keywords": [
"Claude Code skill",
"Douyin search",
"TikTok data extraction",
"social media analytics",
"Codex marketplace",
"video content search",
"Chinese social media",
"data mining tool",
"Claude AI integration",
"marketing analytics"
],
"actual_capabilities": [
"Search Douyin public content (videos and image posts) by keyword in real-time",
"Sort results by comprehensive ranking, most likes, or latest publication",
"Filter results by time range: all time, 1 day, 7 days, or 180 days",
"Extract structured metadata: title, author, likes, comments, shares, collects, URLs",
"Output results in JSON format for programmatic processing or Markdown for human reading",
"Automatic keyword sanitization and parameter validation with retry mechanisms"
],
"limitations": [
"Only accesses Douyin public API data - cannot retrieve private or restricted content",
"Returns CDN links for videos/images but does not download media files directly",
"Rate limited to approximately 2 requests per second to avoid API throttling",
"Requires valid GUAIKEI_API_TOKEN environment variable for API authentication"
],
"use_cases": [
{
"title": "Competitive Analysis for Brands",
"description": "Marketing teams can search competitor brand names or product keywords to analyze their Douyin content strategy, engagement metrics, and posting frequency.",
"target_user": "Brand managers and digital marketing analysts"
},
{
"title": "Trend Research for Content Creators",
"description": "Video creators can search trending topics or hashtags to identify popular content formats, optimal posting times, and audience engagement patterns.",
"target_user": "Social media influencers and content strategists"
},
{
"title": "Academic Research on Social Media",
"description": "Researchers can collect Douyin content data for studies on Chinese social media trends, user behavior analysis, or information diffusion patterns.",
"target_user": "Academic researchers and data scientists"
}
],
"prompt_templates": [
{
"title": "Basic Keyword Search",
"prompt": "Search Douyin for videos about \"AI technology\" and return the top 10 results sorted by comprehensive ranking in JSON format.",
"scenario": "Quick search for general topic exploration"
},
{
"title": "Time-Filtered Search",
"prompt": "Find all Douyin posts about \"electric vehicles\" published within the last 7 days, sorted by most likes, and output as Markdown for my presentation.",
"scenario": "Researching recent trends in a specific industry"
},
{
"title": "Competitor Content Analysis",
"prompt": "Search for content from competitor brand \"Xiaomi\" on Douyin, get 30 results from the past 180 days sorted by latest, export as JSON so I can analyze their posting strategy.",
"scenario": "Competitive intelligence gathering"
},
{
"title": "Engagement Metrics Comparison",
"prompt": "I need to compare engagement rates. Search Douyin for \"cooking tutorial\" with sort by most likes, then search again with comprehensive ranking - both with 20 results in JSON format.",
"scenario": "Data-driven content strategy optimization"
}
],
"output_examples": [
{
"input": "Search for \"AI tutorial\" with default settings",
"output": "Returns JSON with status 'success', 10 video results containing title, author nickname, publish time, video URL, cover image, and engagement counts (likes: 356, comments: 4, collects: 17, shares: 6)"
},
{
"input": "Search for 'cooking recipes' sorted by most likes, past 7 days",
"output": "Returns Markdown formatted list showing 10 cooking videos ranked by engagement, each with author name, publish date, view link, thumbnail, and interaction metrics in readable table format"
}
],
"best_practices": [
"Always use specific, descriptive keywords (2-50 characters) to get relevant search results and avoid API rejection",
"Implement rate limiting in your workflows - wait at least 2 seconds between consecutive API calls to prevent throttling",
"Save and cache search results locally when possible to minimize redundant API requests for the same queries"
],
"anti_patterns": [
"Do not use automated scripts to make rapid consecutive requests - this will trigger API rate limiting and temporary blocks",
"Avoid using the default placeholder token in production - obtain your own GUAIKEI_API_TOKEN for reliable access",
"Do not treat CDN links as permanent - video and image URLs expire, download important content promptly"
],
"faq": [
{
"question": "What is GUAIKEI_API_TOKEN and how do I get one?",
"answer": "GUAIKEI_API_TOKEN is an API key for the guaikei.com Douyin data service. The skill includes a default placeholder token for testing, but for production use you should obtain your own token from the service provider for better rate limits and reliability."
},
{
"question": "Why are some video/image links not working after a few hours?",
"answer": "The skill returns CDN links from Douyin's platform which are temporary and expire after some time. If you need permanent access to media files, you should download them promptly after receiving the search results."
},
{
"question": "Can this skill search private Douyin accounts or deleted content?",
"answer": "No. This skill only accesses publicly available data through the official API. Private accounts, deleted videos, and restricted content cannot be retrieved."
},
{
"question": "What is the maximum number of results I can get per search?",
"answer": "The API limits results to 60 items per request. For larger datasets, you would need to implement pagination or multiple searches with different parameters."
},
{
"question": "Does this skill work with international TikTok content?",
"answer": "No. This skill specifically searches Douyin (the Chinese domestic version of TikTok). International TikTok content requires a different API and is not supported by this skill."
},
{
"question": "How do I output results in Markdown format instead of JSON?",
"answer": "Add the --output markdown flag to your search command. For example: node scripts/search.js AI --output markdown. This produces human-readable formatted output suitable for reports and presentations."
}
]
},
"file_structure": [
{
"name": "lib",
"type": "dir",
"path": "lib",
"children": [
{
"name": "douyin.js",
"type": "file",
"path": "lib/douyin.js",
"lines": 320
},
{
"name": "key.js",
"type": "file",
"path": "lib/key.js",
"lines": 29
},
{
"name": "utils.js",
"type": "file",
"path": "lib/utils.js",
"lines": 33
}
]
},
{
"name": "scripts",
"type": "dir",
"path": "scripts",
"children": [
{
"name": "search.js",
"type": "file",
"path": "scripts/search.js",
"lines": 213
}
]
},
{
"name": "package-lock.json",
"type": "file",
"path": "package-lock.json",
"lines": 13
},
{
"name": "package.json",
"type": "file",
"path": "package.json",
"lines": 25
},
{
"name": "readme.md",
"type": "file",
"path": "readme.md",
"lines": 40
},
{
"name": "SKILL.md",
"type": "file",
"path": "SKILL.md",
"lines": 383
}
]
}
Related skills
How it compares
Use instead of hand-copying Douyin UI results when you want bounded, filterable search exports inside an agent session.
FAQ
Who is douyin-search-keyword for?
creators, growth-minded founders, and agents helping with Chinese short-video content strategy who need programmatic Douyin keyword search.
When should I use douyin-search-keyword?
Use it during Grow content planning when you are scouting topics, during Launch distribution prep when comparing what ranks for a keyword, or anytime you need 1–60 Douyin results with sort and date filters.
Is douyin-search-keyword safe to install?
Review the Security Audits panel on this Prism page and treat the skill as calling external HTTPS APIs; confirm you trust the gateway host and your data handling policy before production use.