面向开发者:本文用可直接运行的 Python / Node.js 代码,演示如何把一条短视频分享链接解析成结构化 JSON,并处理鉴权、出参兼容、错误码与计费边界。
一、先说结论:这是一个同步接口,不是异步任务
很多同学接解析类接口时,第一反应是去找 task_id 和回调。这个接口不用。
| 项目 | 官方口径 |
|---|---|
| 接口地址 | https://xiaoapis.com/api/video/videoparse |
| 请求方式 | GET / POST / JSON 均可 |
| 返回格式 | application/json |
| 响应模型 | 同步,一般 2~5 秒返回 |
| 鉴权 | 请求参数 key(控制台 → 密钥管理) |
| 计费 | 每次成功解析扣 1 点,约 0.01 元/次 |
| 不扣费场景 | 参数错误、解析失败、500 服务内部错误 |
| 频率 | 20 QPS(免费/测试用户 10 QPS 总限),每日次数不限 |
| 免费额度 | 10 次 |
支持平台(官方文档列表):抖音、快手、小红书、哔哩哔哩、微博、皮皮虾、西瓜视频、好看视频、TikTok、YouTube、Instagram、Twitter/X、Facebook、豆包、千问、可灵。
注意:微信视频号是独立接口
/api/video/weixin,不在这个接口的支持列表里,见文末能力索引。
二、请求参数:只有 3 个,但 flat 决定你要写多少兼容代码
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
key |
string | 是 | 接口密钥,控制台 → 密钥管理查看 |
url |
string | 是 | 待解析链接,完整 URL 或分享短链,最大 2048 字符;兼容 content、link;可直接粘贴带网址的分享口令,接口会自动提取链接 |
flat |
int | 否 | 出参结构:2 = 单层 data,type 只输出 视频 / 图集 / 实况图集(默认,推荐);1 = 单层 data,保留平台原始 type;0 = 兼容旧版双层 data |
请求 HEADER:Content-Type: application/x-www-form-urlencoded;charset:utf-8; 或 application/json。
建议新项目一律显式传 flat=2。理由很实际:不同平台对内容类型的命名不一样,用 flat=2 可以把 data.type 收敛成 3 个枚举值,下游分支不用写十几行 if/else。
三、最小可运行示例(Python 标准库,零依赖)
密钥只从服务端环境变量读取,不要写进代码、前端、App 包或公开仓库。
# parse_once.py —— pip 不需要装任何东西,python3 parse_once.py 即可
import json
import os
import urllib.parse
import urllib.request
ENDPOINT = "https://xiaoapis.com/api/video/videoparse"
def parse_video(share_text: str) -> dict:
api_key = os.environ["XIAOAPIS_KEY"] # 服务端环境变量
query = urllib.parse.urlencode({
"key": api_key,
"url": share_text,
"flat": 2,
})
req = urllib.request.Request(
f"{ENDPOINT}?{query}",
headers={
"Accept": "application/json"},
method="GET",
)
with urllib.request.urlopen(req, timeout=15) as resp:
return json.loads(resp.read().decode("utf-8"))
if __name__ == "__main__":
# 可直接粘贴整段分享口令,接口会自动提取其中的 http(s) 地址
result = parse_video("https://v.douyin.com/xxxxx/")
print(json.dumps(result, ensure_ascii=False, indent=2))
运行前:
export XIAOAPIS_KEY='控制台申请的密钥'
python3 parse_once.py
四、生产级封装:错误码分流 + 字段兼容 + 来源展示
官方文档对 data 的几个细节值得单独处理:
data.imagelist/data.images的元素是字符串,不是对象;data.stats的缺项为0或空字符串,不要假设一定有play_count;data.video_url在纯图集时是空字符串,要先判data.type;data.source里的版权说明,官方明确要求调用方在最终用户可见界面展示来源信息。
# parse_client.py
import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request
ENDPOINT = "https://xiaoapis.com/api/video/videoparse"
RETRYABLE_HTTP = {
500, 502, 503, 504}
def _request(params: dict, timeout: int = 15) -> dict:
query = urllib.parse.urlencode(params)
req = urllib.request.Request(
f"{ENDPOINT}?{query}",
headers={
"Accept": "application/json"},
method="GET",
)
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode("utf-8"))
def parse_with_retry(share_text: str, max_retry: int = 2) -> dict:
"""仅对 500/网络异常重试:官方文档说明 500 不扣费,解析失败不扣费。
400/403 属于参数或鉴权问题,重试没有意义,应直接让上层告警。"""
params = {
"key": os.environ["XIAOAPIS_KEY"], "url": share_text, "flat": 2}
for attempt in range(max_retry + 1):
try:
body = _request(params)
code = body.get("code")
if code == 200:
return body
if code == 403:
raise PermissionError("密钥无效或没有权限,检查控制台密钥管理")
if code == 400:
raise ValueError(f"参数错误/链接无法解析/点数不足:{body.get('msg')}")
if code == 500 and attempt < max_retry:
time.sleep(1.5 * (attempt + 1))
continue
raise RuntimeError(f"解析失败 code={code} msg={body.get('msg')}")
except urllib.error.HTTPError as exc:
if exc.code in RETRYABLE_HTTP and attempt < max_retry:
time.sleep(1.5 * (attempt + 1))
continue
raise
except urllib.error.URLError as exc:
if attempt < max_retry:
time.sleep(1.5 * (attempt + 1))
continue
raise RuntimeError(f"网络异常:{exc.reason}") from exc
raise RuntimeError("重试次数耗尽")
def to_material_row(body: dict) -> dict:
"""把解析结果收敛成业务表结构;图集与视频走同一套字段。"""
d = body["data"]
stats = d.get("stats") or {
}
source = d.get("source") or {
}
return {
"platform": d.get("platform", ""),
"content_type": d.get("type", ""),
"title": d.get("title", ""),
"desc": d.get("desc", ""),
"cover_url": d.get("cover_url", ""),
"media_url": d.get("video_url", ""), # 纯图集时为空串
"images": d.get("images") or [], # 元素为字符串
"author": d.get("author") or stats.get("author_name", ""),
"like_count": stats.get("like_count", 0), # 缺项为 0
"play_count": stats.get("play_count", 0),
"publish_time": stats.get("publish_time", ""),
# 以下三项请在最终用户可见界面展示
"source_platform": source.get("platform_label", ""),
"source_author": source.get("author_name", ""),
"copyright_notice": source.get("copyright_notice", ""),
}
if __name__ == "__main__":
row = to_material_row(parse_with_retry("https://v.douyin.com/xxxxx/"))
print(json.dumps(row, ensure_ascii=False, indent=2))
Node.js 版本(Node 18+ 内置 fetch)
// parse.mjs —— XIAOAPIS_KEY=xxx node parse.mjs
const endpoint = "https://xiaoapis.com/api/video/videoparse";
export async function parseVideo(shareText) {
const params = new URLSearchParams({
key: process.env.XIAOAPIS_KEY,
url: shareText,
flat: "2",
});
const res = await fetch(`${
endpoint}?${
params}`, {
signal: AbortSignal.timeout(15000),
});
const body = await res.json();
if (body.code !== 200) {
throw new Error(`code=${
body.code} msg=${
body.msg}`);
}
return body.data;
}
const data = await parseVideo("https://v.douyin.com/xxxxx/");
console.log(data.title, data.video_url || data.images);
五、返回结构(以下为官方文档示例的精简摘录)
{
"code": 200,
"msg": "success",
"data": {
"platform": "xiaohongshu",
"type": "视频",
"title": "一年级同学你好!!!",
"desc": "这个小朋友真的成为小学生了,送到校门口老母亲就飙泪",
"video_url": "https://sns-video-hs.xhscdn.com/example.mp4",
"cover_url": "https://sns-img-hw.xhscdn.com/example",
"images": ["https://sns-img-hw.xhscdn.com/example"],
"author": "示例作者",
"video_format": "mp4",
"stats": {
"like_count": 40,
"comment_count": 23,
"share_count": 4,
"play_count": 0,
"collect_count": 9,
"publish_time": "2026-08-30 08:59:24"
},
"video_list": [
{
"quality": "高清", "url": "https://sns-video-hs.xhscdn.com/example.mp4", "size": "", "resolution": "1080x1920" }
],
"source": {
"platform_label": "小红书",
"copyright_notice": "本内容版权归原作者及原平台所有……",
"usage_restriction": "仅供个人备份、内容审核、数据分析、学术研究等合理使用场景……"
}
},
"exec_time": 2.18,
"ip": "1.2.3.4"
}
顶层还有 exec_time(执行耗时)与 ip(客户端 IP)。data.video_list 给出全部可下载清晰度档,含 quality / url / size / resolution;data.source 给出平台中文名、原始链接、作者与版权/使用限制说明。
需要注意的是,stats.play_count 在这份官方示例里就是 0——互动数据存在缺项或平台不返回的情况,业务侧不要把它当成必填指标,更不要在报表里直接除以它。
六、错误码与处理策略
| 业务码 | 官方说明 | 建议动作 |
|---|---|---|
200 |
成功 | 落库,保留 source 来源信息 |
400 |
参数错误、链接无法访问或未能解析到有效内容;点数不足也会返回 400 | 校验链接与余额,不重试 |
403 |
没有权限 / 密钥无效 | 检查服务端密钥配置,不重试 |
500 |
服务内部错误,请稍后重试;此类错误不扣费 | 有限次退避重试,仍失败则告警 |
计费优先级(官方原文):企业会员 -> 单独包月 -> 次数包 -> 点数计费 -> 账户余额 -> 免费额度。
七、两个真实使用场景
场景 1|内容素材库批量入库。 运营同学从各平台复制一批分享口令,后台按 flat=2 逐条解析,把 platform / type / title / cover_url / images / author / publish_time 写入素材表;source.copyright_notice 随素材一起存,并在素材详情页展示。整个过程是同步的,一个请求一个结果,不需要任务队列。要注意按 20 QPS 做令牌桶节流,免费/测试账号按 10 QPS 限。
场景 2|爆款拆解与二次创作的上游。 先用本接口拿到 video_url 与作者信息,再把媒体地址交给「短视频提取文案解析」接口得到 resultText,或交给「视频反推提示词」接口得到镜头/光影描述,最后进入「AI 视频切片」做拆条。这条链路的价值在于:每一步的输入都是上一步的结构化字段,不需要人工下载、转存、重命名。
合规边界:官方明确该接口仅提供链接元数据解析,不存储、不复制、不分发原始内容;解析结果版权归原作者及原平台所有,仅供个人备份、内容审核、数据分析、学术研究等合理使用,禁止公开传播他人作品、二次商业转售或集成到下载分发站点。
八、FAQ
Q:视频解析 API 支持哪些平台?微信视频号能用这个接口吗?
A:官方文档列出的 16 个平台见第一节表格。微信视频号使用独立接口 /api/video/weixin,两者不是同一个地址,计费和频率限制也不同。
Q:分享口令里混着中文和表情,需要先正则提取 URL 吗?
A:url 参数支持直接粘贴带链接的分享口令,接口会自动提取其中的 http(s) 地址。生产环境仍建议先做一次长度与协议校验(上限 2048 字符),避免把无关文本传进去。
Q:解析失败会扣费吗?
A:按官方文档,参数错误、解析失败以及 500 服务内部错误都不扣费;只有成功解析才扣 1 点。所以重试策略应只覆盖 500 与网络异常,不要对 400 反复重试。
Q:flat=0/1/2 该选哪个?
A:新项目用 flat=2(默认)。flat=1 保留平台原始 type,适合需要原始类型做分析的场景;flat=0 是旧版双层 data 兼容模式,仅用于存量系统迁移。
Q:能不能把 API Key 放在前端直接调?
A:不能。密钥只应放在服务端环境变量或密钥管理系统中,由后端代理转发;前端直连等于把密钥公开,且无法做调用审计与限流。