TikTokDownload API接口全解析:开发者必备参考
你是否还在为抖音(TikTok)内容批量下载的复杂流程而困扰?是否因API接口文档缺失而无法高效开发?本文将系统解析TikTokDownload项目的API接口体系,从用户信息获取到作品批量下载,从参数配置到错误处理,提供一站式解决方案。读完本文,你将获得:
- 7个核心API接口的详细调用指南
- 10+实用参数配置技巧
- 完整的错误码对照表
- 3种语言的调用示例代码
- 高并发场景的性能优化方案
项目概述
TikTokDownload是一个专注于抖音内容去水印批量下载的开源项目,支持用户主页作品、喜欢、收藏、图文、音频等多种资源类型的获取。其API接口体系基于RESTful设计规范,提供了简洁易用的接口调用方式,开发者可快速集成到自己的应用中。
API接口详解
1. 用户基本信息接口
接口路径:/API/user_base_info.json
请求方法:GET
功能描述:获取指定用户的基本信息,包括头像、昵称、签名、关注状态等核心数据。
请求参数:
| 参数名 | 类型 | 必选 | 描述 | 示例值 |
|---|---|---|---|---|
| sec_uid | string | 是 | 用户安全ID | MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o |
| unique_id | string | 否 | 用户唯一ID | 777_23 |
响应示例:
{
"status_code": 0,
"data": [
{
"uid": "89964046416",
"short_id": "629064951",
"nickname": "蔓越莓烦恼",
"avatar_thumb": {
"uri": "100x100/aweme-avatar/tos-cn-avt-0015_9c8aea4ba21f591774723a3820a07aef",
"url_list": [
"https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_9c8aea4ba21f591774723a3820a07aef.jpeg?from=3782654143",
"https://p6.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_9c8aea4ba21f591774723a3820a07aef.jpeg?from=3782654143",
"https://p11.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_9c8aea4ba21f591774723a3820a07aef.jpeg?from=3782654143"
]
},
"signature": "风格多变的小🦁️\n🧣:yoki小蔓\n🍠:同名",
"follow_status": 1,
"unique_id": "777_23",
"sec_uid": "MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o"
}
]
}
字段说明:
| 字段名 | 类型 | 描述 |
|---|---|---|
| uid | string | 用户数字ID |
| short_id | string | 用户短ID |
| nickname | string | 用户昵称 |
| avatar_thumb | object | 头像缩略图信息 |
| signature | string | 用户签名 |
| follow_status | integer | 关注状态(0:未关注,1:已关注) |
| unique_id | string | 用户唯一ID |
| sec_uid | string | 用户安全ID |
2. 视频作品信息接口
接口路径:/API/user_post_info_video.json
请求方法:GET
功能描述:获取用户发布的视频作品列表,支持分页获取,返回视频的详细信息和下载地址。
请求参数:
| 参数名 | 类型 | 必选 | 描述 | 示例值 |
|---|---|---|---|---|
| sec_uid | string | 是 | 用户安全ID | MS4wLjABAAAAdBrGekUlNXGPsc8rNnv_jDhN8koyuWDyrU3CrcvzmcQ |
| count | integer | 否 | 每页数量,默认20 | 20 |
| cursor | integer | 否 | 分页游标,第一页为0 | 0 |
响应示例:
{
"status_code": 0,
"min_cursor": 1689909551000,
"max_cursor": 1689909551000,
"has_more": 1,
"aweme_list": [
{
"aweme_id": "7253290037918354691",
"desc": "飞行员跳伞进影视基地,哪知道白捡了个寨主当 #电影冰雪狙击2 #冰雪狙击2老戏骨飙戏",
"create_time": 1688788818,
"video": {
"play_addr": {
"uri": "v0200fg10000cikdoqjc77u487ia3t1g",
"url_list": [
"http://v26-web.douyinvod.com/f09adc21743f08884ebfd18bee2c7757/64ba6b84/video/tos/cn/tos-cn-ve-15c001-alinc2/oMSoq9gqbAIgtxOKQn9keEBD4DACeAMhgNREnI/?a=6383&ch=10010&cr=0&dr=0&lr=all&cd=0|0|0|0&cv=1&br=2198&bt=2198&cs=0&ds=4&ft=bvTKJbQQqUWXf_40mo0OW_EklpPiXwUEiMVJEAmEA.CPD-I&mime_type=video_mp4&qs=0&rc=NzhnOmg5ODc7O2U5ZTVmZ0BpandkOjc6ZnRnbDMzNGkzM0AtX2NeXmJjNl4xLTRfL141YSMuMGY1cjRnbGhgLS1kLS9zcw==&l=202307211825566527AF3C1671F5138610&btag=e00028000&dy_q=1689935156",
"http://v3-web.douyinvod.com/a5e31e04504bc6463031a9d4036d643d/64ba6b84/video/tos/cn/tos-cn-ve-15c001-alinc2/oMSoq9gqbAIgtxOKQn9keEBD4DACeAMhgNREnI/?a=6383&ch=10010&cr=0&dr=0&lr=all&cd=0|0|0|0&cv=1&br=2198&bt=2198&cs=0&ds=4&ft=bvTKJbQQqUWXf_40mo0OW_EklpPiXwUEiMVJEAmEA.CPD-I&mime_type=video_mp4&qs=0&rc=NzhnOmg5ODc7O2U5ZTVmZ0BpandkOjc6ZnRnbDMzNGkzM0AtX2NeXmJjNl4xLTRfL141YSMuMGY1cjRnbGhgLS1kLS9zcw==&l=202307211825566527AF3C1671F5138610&btag=e00028000&dy_q=1689935156"
],
"width": 1728,
"height": 1080,
"data_size": 18025814
},
"cover": {
"uri": "tos-cn-i-dy/bc4a6c96faea452e983b1371561e853d",
"url_list": [
"https://p3-pc-sign.douyinpic.com/tos-cn-i-dy/bc4a6c96faea452e983b1371561e853d~tplv-dy-cropcenter:323:430.jpeg?x-expires=1691143200&x-signature=M/mIef2TBxaBIg5VGxQtKuAK/5U=&from=3213915784&s=PackSourceEnum_PUBLISH&se=true&sh=323_430&sc=cover&biz_tag=pcweb_cover&l=202307211825566527AF3C1671F5138610"
],
"width": 720,
"height": 720
},
"height": 1200,
"width": 1920,
"ratio": "1080p"
},
"statistics": {
"comment_count": 166,
"digg_count": 20629,
"collect_count": 1572,
"share_count": 406
}
}
]
}
分页机制:
该接口采用游标分页机制,通过cursor参数控制分页位置,has_more字段表示是否还有更多数据。分页流程如下:
视频质量选择:
接口返回多种清晰度的视频地址,可根据需求选择:
| 清晰度 | gear_name | bit_rate | 适用场景 |
|---|---|---|---|
| 1080P | normal_1080_0 | 2251713 | 高清展示 |
| 720P | normal_720_0 | 1444459 | 平衡质量和流量 |
| 540P | normal_540_0 | 1305609 | 普通观看 |
| 低清 | lower_540_0 | 872751 | 省流量模式 |
3. 图文作品信息接口
接口路径:/API/user_post_info_image.json
请求方法:GET
功能描述:获取用户发布的图文作品列表,支持分页获取,返回图文的详细信息和图片下载地址。
请求参数:
| 参数名 | 类型 | 必选 | 描述 | 示例值 |
|---|---|---|---|---|
| sec_uid | string | 是 | 用户安全ID | MS4wLjABAAAA-hFsLR8fSnbmWhrmAEnGvYgG6rM2txNpNm2oT9WSrIQ |
| count | integer | 否 | 每页数量,默认20 | 20 |
| cursor | integer | 否 | 分页游标,第一页为0 | 0 |
响应示例:
{
"status_code": 0,
"min_cursor": 1689909551000,
"max_cursor": 1689909551000,
"has_more": 1,
"aweme_list": [
{
"aweme_id": "7232626237586312485",
"desc": "辣妹统治世界٩(˃̶͈̀௰˂̶͈́)و。#ootd穿搭 #氛围感 #女友穿搭#辣妹穿搭 #生活碎片",
"create_time": 1683977029,
"share_url": "https://www.iesdouyin.com/share/video/7232626237586312485/?region=CN&mid=7203284398119225346&u_code=l1j9bkbd",
"statistics": {
"comment_count": 166,
"digg_count": 20629,
"collect_count": 1572,
"share_count": 406
},
"text_extra": [
{
"start": 20,
"end": 27,
"type": 1,
"hashtag_name": "ootd穿搭",
"hashtag_id": "1645837370349579"
},
{
"start": 29,
"end": 33,
"type": 1,
"hashtag_name": "氛围感",
"hashtag_id": "1640936621124619"
}
]
}
]
}
4. 作品详情接口
接口路径:/API/user_post_detail.json
请求方法:GET
功能描述:获取单个作品的详细信息,包括完整的评论、点赞、分享等互动数据。
请求参数:
| 参数名 | 类型 | 必选 | 描述 | 示例值 |
|---|---|---|---|---|
| aweme_id | string | 是 | 作品ID | 7253290037918354691 |
5. 作品删除接口
接口路径:/API/user_post_delete.json
请求方法:POST
功能描述:删除用户发布的作品,需要用户授权。
请求参数:
| 参数名 | 类型 | 必选 | 描述 | 示例值 |
|---|---|---|---|---|
| aweme_id | string | 是 | 作品ID | 7253290037918354691 |
| sec_uid | string | 是 | 用户安全ID | MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o |
接口调用示例
Python调用示例
import requests
import json
def get_user_videos(sec_uid, max_count=50):
"""
获取用户视频作品列表
:param sec_uid: 用户安全ID
:param max_count: 最大获取数量
:return: 视频列表
"""
videos = []
cursor = 0
base_url = "https://api.example.com/API/user_post_info_video.json"
while len(videos) < max_count:
params = {
"sec_uid": sec_uid,
"count": min(20, max_count - len(videos)),
"cursor": cursor
}
response = requests.get(base_url, params=params)
data = json.loads(response.text)
if data.get("status_code") != 0:
print(f"请求错误: {data}")
break
aweme_list = data.get("aweme_list", [])
videos.extend(aweme_list)
if data.get("has_more") != 1:
break
cursor = data.get("max_cursor", 0)
return videos[:max_count]
# 使用示例
sec_uid = "MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o"
videos = get_user_videos(sec_uid, max_count=30)
print(f"获取到 {len(videos)} 个视频作品")
# 下载第一个视频
if videos:
first_video = videos[0]
video_url = first_video["video"]["play_addr"]["url_list"][0]
response = requests.get(video_url, stream=True)
with open(f"{first_video['aweme_id']}.mp4", "wb") as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
print(f"视频下载完成: {first_video['aweme_id']}.mp4")
JavaScript调用示例
/**
* 获取用户视频作品列表
* @param {string} sec_uid - 用户安全ID
* @param {number} max_count - 最大获取数量
* @returns {Promise<Array>} 视频列表
*/
async function getUserVideos(sec_uid, max_count = 50) {
const videos = [];
let cursor = 0;
const base_url = "https://api.example.com/API/user_post_info_video.json";
while (videos.length < max_count) {
const count = Math.min(20, max_count - videos.length);
const url = new URL(base_url);
url.searchParams.set("sec_uid", sec_uid);
url.searchParams.set("count", count);
url.searchParams.set("cursor", cursor);
try {
const response = await fetch(url);
const data = await response.json();
if (data.status_code !== 0) {
console.error("请求错误:", data);
break;
}
const aweme_list = data.aweme_list || [];
videos.push(...aweme_list);
if (data.has_more !== 1) {
break;
}
cursor = data.max_cursor || 0;
} catch (error) {
console.error("请求失败:", error);
break;
}
}
return videos.slice(0, max_count);
}
// 使用示例
const sec_uid = "MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o";
getUserVideos(sec_uid, 30)
.then(videos => {
console.log(`获取到 ${videos.length} 个视频作品`);
// 下载第一个视频
if (videos.length > 0) {
const firstVideo = videos[0];
const videoUrl = firstVideo.video.play_addr.url_list[0];
fetch(videoUrl)
.then(response => response.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${firstVideo.aweme_id}.mp4`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log(`视频下载完成: ${firstVideo.aweme_id}.mp4`);
});
}
});
Java调用示例
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
public class TikTokApiClient {
private static final String BASE_URL = "https://api.example.com/API/user_post_info_video.json";
/**
* 获取用户视频作品列表
* @param secUid 用户安全ID
* @param maxCount 最大获取数量
* @return 视频列表
*/
public List<JSONObject> getUserVideos(String secUid, int maxCount) {
List<JSONObject> videos = new ArrayList<>();
int cursor = 0;
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
while (videos.size() < maxCount) {
int count = Math.min(20, maxCount - videos.size());
URIBuilder builder = new URIBuilder(BASE_URL);
builder.setParameter("sec_uid", secUid);
builder.setParameter("count", String.valueOf(count));
builder.setParameter("cursor", String.valueOf(cursor));
URI uri = builder.build();
HttpGet httpget = new HttpGet(uri);
try (CloseableHttpResponse response = httpclient.execute(httpget)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
String responseString = EntityUtils.toString(entity);
JSONObject data = JSON.parseObject(responseString);
if (data.getInteger("status_code") != 0) {
System.err.println("请求错误: " + data);
break;
}
JSONArray awemeList = data.getJSONArray("aweme_list");
if (awemeList != null) {
for (int i = 0; i < awemeList.size(); i++) {
videos.add(awemeList.getJSONObject(i));
}
}
if (data.getInteger("has_more") != 1) {
break;
}
cursor = data.getInteger("max_cursor");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return videos.subList(0, Math.min(videos.size(), maxCount));
}
/**
* 下载视频
* @param videoUrl 视频URL
* @param fileName 保存文件名
*/
public void downloadVideo(String videoUrl, String fileName) {
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
HttpGet httpget = new HttpGet(videoUrl);
try (CloseableHttpResponse response = httpclient.execute(httpget)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
try (InputStream in = entity.getContent();
FileOutputStream out = new FileOutputStream(fileName)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
System.out.println("视频下载完成: " + fileName);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TikTokApiClient client = new TikTokApiClient();
String secUid = "MS4wLjABAAAADgOltsr_SYTX6VurKA1H3n61xlFvYD1y_FrrY1UhI-o";
List<JSONObject> videos = client.getUserVideos(secUid, 30);
System.out.println("获取到 " + videos.size() + " 个视频作品");
if (!videos.isEmpty()) {
JSONObject firstVideo = videos.get(0);
String videoUrl = firstVideo.getJSONObject("video")
.getJSONObject("play_addr")
.getJSONArray("url_list")
.getString(0);
client.downloadVideo(videoUrl, firstVideo.getString("aweme_id") + ".mp4");
}
}
}
错误码及解决方案
| 错误码 | 描述 | 可能原因 | 解决方案 |
|---|---|---|---|
| 0 | 成功 | 请求正常 | - |
| 10001 | 参数错误 | 参数缺失或格式错误 | 检查必填参数,确保格式正确 |
| 10002 | 授权失败 | 用户未授权或授权过期 | 重新获取用户授权 |
| 10003 | 访问频率限制 | API调用过于频繁 | 降低调用频率,实现合理的重试机制 |
| 10004 | 资源不存在 | 请求的资源已被删除或不存在 | 检查资源ID是否正确 |
| 10005 | 服务器内部错误 | 服务器处理异常 | 稍后重试,如持续出现联系技术支持 |
错误处理最佳实践:
def safe_api_request(url, params):
"""带错误处理的API请求"""
max_retries = 3
retry_delay = 2 # 秒
for attempt in range(max_retries):
try:
response = requests.get(url, params=params, timeout=10)
data = json.loads(response.text)
if data.get("status_code") == 0:
return data
elif data.get("status_code") == 10003:
print(f"访问频率限制,将在 {retry_delay} 秒后重试")
time.sleep(retry_delay)
retry_delay *= 2 # 指数退避
else:
print(f"API错误: {data.get('status_code')}, 消息: {data.get('message')}")
return None
except requests.exceptions.RequestException as e:
print(f"请求异常: {e}")
if attempt < max_retries - 1:
print(f"将在 {retry_delay} 秒后重试")
time.sleep(retry_delay)
retry_delay *= 2
print(f"达到最大重试次数 {max_retries}")
return None
性能优化建议
1. 批量请求优化
- 减少请求次数:合理设置
count参数,最大可设为50,减少分页请求次数 - 并发请求:使用多线程/多协程并发请求不同用户的数据,但需控制并发数
- 请求合并:对于多个独立的资源请求,考虑合并为批量请求(如支持)
2. 缓存策略
3. 下载优化
- 断点续传:支持大文件断点续传,避免网络中断后重新下载
- 分片下载:大文件采用分片下载,提高下载速度
- CDN加速:使用CDN加速下载,选择距离用户最近的节点
安全注意事项
- 用户数据保护:获取的用户数据需严格保密,不得泄露或用于未授权用途
- API密钥管理:不要在客户端代码中硬编码API密钥,使用后端代理
- 请求签名:敏感操作需使用请求签名机制,防止请求被篡改
- 合规使用:遵守抖音API使用规范,合理使用API,避免滥用
总结与展望
TikTokDownload API接口提供了全面的抖音内容获取能力,通过本文的解析,开发者可以快速掌握各接口的使用方法和最佳实践。无论是构建内容分析工具、批量下载应用还是二次创作平台,这些API都能提供坚实的技术支持。
项目未来可能的发展方向:
- 增加直播内容获取接口
- 支持更多筛选条件的高级搜索
- 提供AI内容分析功能
- 增强数据导出和报告生成能力
建议开发者持续关注项目更新,及时了解新功能和接口变化。如有任何问题或需求,可以通过项目的Issue系统反馈。
如果你觉得本文对你有帮助,请点赞、收藏、关注三连,以便获取更多API使用技巧和最佳实践。下期预告:《TikTokDownload高级应用:从数据爬取到内容分析》
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



