HTTP API #
SaveAny-Bot 提供 HTTP API,支持通过脚本或第三方程序创建下载/转存任务、查询状态及取消任务。
启用 API #
API 与 Web 控制台共用监听地址和端口,不需要单独启用。Web 控制台默认监听 127.0.0.1:28080。在 设置 → Web 中配置访问 Token:
访问 Token:用于/?token=...直接登录 Web UI,也用于 API 的Authorization: Bearer <token>。监听地址:控制台与 API 共用;需要从其他主机访问时使用0.0.0.0:28080。
所有接口位于 /api/v1 下。
认证 #
在 设置 → Web 中配置访问 Token,然后在请求头中携带:
Authorization: Bearer <your-token>
未配置访问 Token 时,每次启动都会生成随机 Token,并作为 Web UI 直达地址的一部分打印到日志。该 Token 在当前进程内同样可用于 Bearer 认证。
也可以使用 Web 控制台登录 Cookie。使用 Cookie 的非 GET 请求还需要携带 X-CSRF-Token。
鉴权失败时返回 401:
{ "error": "unauthorized", "message": "authentication required" }
错误响应格式 #
所有错误均使用统一的 JSON 格式:
{
"error": "error_code",
"message": "错误说明"
}
常见错误码:
| 错误码 | HTTP 状态 | 含义 |
|---|---|---|
unauthorized | 401 | 鉴权失败 |
invalid_request | 400 | 请求体/参数非法 |
task_creation_failed | 400 | 任务创建失败 |
task_not_found | 404 | 任务 ID 不存在 |
cancel_failed | 500 | 取消任务失败 |
internal_error | 500 | 服务器内部错误 |
接口列表 #
GET /health — 健康检查 #
无需鉴权。
响应 200 OK:
{ "status": "ok", "version": "1.0.0" }
GET /api/v1/storages — 列出存储 #
返回当前所有已加载的存储后端。
响应 200 OK:
{
"storages": [
{
"storage_id": 1,
"name": "local",
"type": "local",
"enable": true,
"config": { "base_path": "/app/downloads" },
"config_version": 1
}
]
}
GET /api/v1/meta — API 与能力元数据 #
返回冻结的 v1 能力词表和数据库 schema 版本。
{
"api_version": "v1",
"version": "1.0.0",
"schema_version": 1,
"chat_id_format": "bot_api",
"task_types": ["tgfiles", "tphpics", "parseditem", "directlinks", "aria2", "ytdlp", "transfer"],
"task_statuses": ["queued", "running", "completed", "partial", "failed", "cancelled"],
"task_item_statuses": ["completed", "failed", "skipped", "cancelled"],
"storage_types": ["local", "webdav", "alist", "telegram", "s3", "rclone"],
"rule_types": ["FILENAME-REGEX", "MESSAGE-REGEX", "IS-ALBUM"],
"filename_strategies": ["default", "message", "template"],
"conflict_strategies": ["rename", "ask", "overwrite", "skip"]
}
聊天 ID 使用 Bot API 形式:用户为正数,普通群为负数,超级群和频道使用 -100...。
GET /api/v1/task-types — 列出支持的任务类型 #
响应 200 OK:
{
"types": [
{ "type": "directlinks", "label": "directlinks" },
{ "type": "tgfiles", "label": "tgfiles" }
]
}
GET /api/v1/media-metadata — 媒体元数据查询 #
探测 Telegram 消息链接、直链音视频或 yt-dlp 支持的媒体链接,无需完整下载即可提取标题、缩略图、上传者和时长等元数据。
请求:
curl -X GET "http://127.0.0.1:28080/api/v1/media-metadata?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dxxx" \
-H "Authorization: Bearer YOUR_TOKEN"
响应:
{
"url": "https://www.youtube.com/watch?v=xxx",
"title": "示例标题",
"thumbnail": "https://example.com/thumb.jpg",
"uploader": "示例上传者",
"duration_seconds": 213.0
}
当缺少 url 参数时返回 400 invalid_request;当 Telegram、直链探测和 yt-dlp 都无法获取该链接时返回 400 metadata_extraction_failed。
POST /api/v1/tasks — 创建任务 #
请求头:
Content-Type: application/json
Authorization: Bearer <token>
请求体:
{
"type": "<任务类型>",
"storage_id": 1,
"path": "<子目录>",
"webhook": "<回调URL>",
"params": { }
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 是 | 任务类型,见下文 |
storage_id | uint | 是 | 目标存储 ID |
path | string | 否 | 存储内的子目录路径 |
webhook | string | 否 | 任务完成/失败时的回调地址 |
params | object | 是 | 各任务类型的专属参数,见下文 |
响应 201 Created:
{
"task_id": "abc123xyz",
"type": "directlinks",
"status": "queued",
"storage_id": 1,
"storage_name": "local",
"created_at": "2026-03-11T10:00:00Z"
}
任务类型与 params #
directlinks — 直接下载链接 #
下载一个或多个 HTTP/HTTPS 直链文件。
{
"type": "directlinks",
"storage_id": 1,
"path": "downloads",
"params": {
"urls": [
"https://example.com/file.zip",
"https://example.com/other.zip"
]
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
urls | []string | 是 | 下载地址列表,至少 1 条 |
ytdlp — yt-dlp 视频下载 #
需要在系统中安装 yt-dlp。
通过 yt-dlp 下载音视频,支持主流视频网站。
{
"type": "ytdlp",
"storage_id": 1,
"path": "videos",
"params": {
"urls": ["https://www.youtube.com/watch?v=xxx"],
"flags": ["--extract-audio", "--audio-format", "mp3"]
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
urls | []string | 是 | 媒体链接列表,至少 1 条 |
flags | []string | 否 | 额外的 yt-dlp 命令行参数; -o/--output 仅使用其文件名模板 (下载目录由 bot 管理) |
aria2 — Aria2 下载 #
需要在 **设置 → Aria2** 中启用并配置 Aria2 RPC。
通过 Aria2 下载管理器下载文件,支持 HTTP/HTTPS、FTP、BitTorrent(磁力链接、种子)等协议。
{
"type": "aria2",
"storage_id": 1,
"path": "downloads",
"params": {
"urls": ["magnet:?xt=urn:btih:..."],
"options": { "split": "4" }
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
urls | []string | 是 | 下载地址列表,至少 1 条 |
options | map[string]string | 否 | Aria2 下载选项 |
parseditem — 解析器下载 #
将 URL 交由已注册的 JS 插件或内置解析器处理后下载。
{
"type": "parseditem",
"storage_id": 1,
"path": "parsed",
"params": {
"url": "https://some-site.com/page"
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | 待解析的页面 URL |
若没有任何解析器能处理该 URL,则返回 400 task_creation_failed。
tgfiles — Telegram 消息文件下载 #
通过 Telegram 消息链接下载文件。支持以下链接格式:
https://t.me/username/123— 公开频道/群组https://t.me/c/123456789/123— 私有频道(数字 ID)https://t.me/c/123456789/111/456— 话题消息https://t.me/username/111/456— 用户名频道下的话题消息
若消息属于媒体组(相册),默认下载整组文件。在链接末尾追加 ?single 可强制只下载单条消息的文件。
{
"type": "tgfiles",
"storage_id": 1,
"path": "telegram",
"params": {
"message_links": [
"https://t.me/username/123",
"https://t.me/c/1234567890/456"
]
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
message_links | []string | 否 | Telegram 消息链接列表;与 chat_id + message_range 二选一 |
chat_id | string | 否 | 与消息范围配合使用的 Bot API 聊天 ID 或用户名 |
message_range | string | 否 | 消息范围,例如 100-120 |
filter | string | 否 | 可选正则,匹配消息文本或文件名 |
tphpics — Telegraph 文章图片下载 #
下载 Telegra.ph 文章中的所有图片。
支持的链接前缀:https://telegra.ph/、http://telegra.ph/、https://telegraph.co/、http://telegraph.co/
{
"type": "tphpics",
"storage_id": 1,
"path": "telegraph",
"params": {
"telegraph_url": "https://telegra.ph/Some-Article-01-01"
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
telegraph_url | string | 是 | Telegra.ph 文章 URL |
transfer — 存储间文件传输 #
在两个存储后端之间直接传输文件,无需经过 Telegram。源存储须支持列举(list)和读取(read)操作。
`transfer` 任务中,顶层的 `storage_id` 字段仍然必须填写(用于通过参数校验),但实际使用的存储由 `params` 中的 `source_storage_id` 和 `target_storage_id` 决定。
{
"type": "transfer",
"storage_id": 1,
"params": {
"source_storage_id": 2,
"source_path": "backups/",
"target_storage_id": 1,
"target_path": "restored/"
}
}
| params 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
source_storage_id | uint | 是 | 源存储 ID |
source_path | string | 是 | 源存储中的路径,须包含至少一个文件 |
target_storage_id | uint | 是 | 目标存储 ID |
target_path | string | 是 | 目标存储中的路径 |
GET /api/v1/tasks — 列出所有任务 #
返回所有任务,按创建时间由新到旧排序。任务记录和创建参数持久化在 SQLite 中;排队中和中断的任务会在重启后恢复。终态记录保留到通过 API 删除。
响应 200 OK:
{
"tasks": [
{
"task_id": "abc123xyz",
"type": "directlinks",
"status": "running",
"title": "file.zip",
"storage_id": 1,
"storage_name": "local",
"path": "downloads",
"error": "",
"created_at": "2026-03-11T10:00:00Z",
"updated_at": "2026-03-11T10:00:05Z",
"progress": {
"total_bytes": 10485760,
"downloaded_bytes": 5242880,
"total_files": 3,
"downloaded_files": 1,
"percent": 50.0,
"speed_mbps": 12.5
}
}
],
"total": 1
}
progress 字段在 total_bytes 或 total_files 大于 0 时出现,其内部字段为 0 时会被省略。percent 在已知字节总量时按字节计算,否则回退为按文件数计算。speed_mbps 是任务开始运行以来的平均速度,而非瞬时速度。error 字段仅在有错误时出现。
GET /api/v1/tasks/{task_id} — 查询任务 #
路径参数: task_id — 创建任务时返回的 ID。
响应 200 OK: 同上列表中的单个任务对象。
错误响应:
400 invalid_request— 路径中未提供 task_id404 task_not_found— 任务不存在
任务对象在至少一个文件保存了消息元数据时会返回 metadata_available。
GET /api/v1/tasks/{task_id}/metadata — 查询消息元数据 #
返回 Telegram 文件任务中每个已保存文件的消息元数据。需要先在 设置 → 消息元数据 中启用;元数据始终保存在本地数据库。启用“写入旁车 JSON”时还会在实际文件旁写入 <文件路径>.meta.json,Telegram 存储会跳过旁车。
路径参数: task_id — 要查询的任务。
响应 200 OK:
{
"task_id": "abc123xyz",
"items": [
{
"index": 1,
"name": "saved-video.mp4",
"path": "downloads/saved-video.mp4",
"metadata": {
"version": 1,
"message": {
"id": 123,
"chat": { "id": -1001234567890, "title": "Channel" },
"sender": { "id": 123456789, "name": "Alice" },
"text": "caption",
"grouped_id": 999
},
"file": {
"name": "saved-video.mp4",
"media_type": "document",
"mime_type": "video/mp4",
"size": 12345678,
"width": 1920,
"height": 1080,
"duration": 123.4
}
}
}
]
}
关闭“保存消息文本”时不会保存消息正文。不会保存 access hash、Token 或原始 Telegram 对象。
错误响应:
400 invalid_request— 路径中未提供 task_id404 task_not_found— 任务不存在404 metadata_not_found— 该任务没有已记录的元数据
POST /api/v1/tasks/{task_id}/retry — 重试失败文件 #
使用已完成任务中失败的文件创建新任务。Bot 会重新拉取原始消息,同时刷新文件引用。仅支持 Telegram 文件任务(tgfiles)。
路径参数: task_id — 要重试的任务。
响应 202 Accepted:
{ "task_id": "d4f7u1crvimcs4n4j8e1", "status": "queued", "file_count": 2 }
错误响应:
409 task_active— 任务仍在排队或运行中409 nothing_to_retry— 没有失败或未完成的文件409 storage_missing/409 source_unavailable400 retry_unsupported— 该任务类型不支持重试503 client_unavailable— 没有运行中的 Telegram 客户端
任务响应包含单文件结果:列表响应返回失败总数(failed_count)以及每个任务最多 5 个失败项(failed_items),单任务查询返回全部记录(items)。
POST /api/v1/tasks/{task_id}/cancel — 取消任务 #
取消排队中或运行中的任务。
路径参数: task_id — 要取消的任务。
响应 202 Accepted:
{ "task_id": "abc123xyz", "status": "cancelled" }
错误响应:
409 task_not_active— 任务已经结束404 task_not_found— 任务不存在500 cancel_failed— 取消操作失败
DELETE /api/v1/tasks/{task_id} — 删除任务 #
删除已结束的任务及其记录。取消排队中或运行中的任务请使用 POST /api/v1/tasks/{task_id}/cancel。
路径参数: task_id
响应 204 No Content:
409 task_active— 请先取消任务404 task_not_found— 任务不存在
任务状态 #
| 状态值 | 含义 |
|---|---|
queued | 已入队,等待执行 |
running | 正在执行 |
completed | 已成功完成 |
partial | 已完成,但部分文件失败 |
failed | 执行失败 |
cancelled | 已通过取消接口取消 |
排队中的任务和进程停止时正在运行的任务都会保存创建参数,重启后自动重新入队。Telegram 下载会从部分缓存继续,已完成上传的文件会被跳过。
Webhook 回调 #
创建任务时可设置 webhook 字段。当任务进入 completed、partial 或 failed 状态时,Bot 会向该地址发送一个 POST 请求。
已取消的任务不会触发 Webhook,每个任务最多发送一次回调。
回调为异步发送,不会因为回调地址响应缓慢或不可达而阻塞任务。
回调请求头:
Content-Type: application/json
User-Agent: SaveAny-Bot/1.0
回调请求体:
{
"task_id": "abc123xyz",
"type": "directlinks",
"status": "completed",
"storage_id": 1,
"storage_name": "local",
"path": "downloads",
"completed_at": "2026-03-11T10:01:00Z",
"error": ""
}
completed_at 仅在状态为 completed、partial 或 failed 时出现。error 仅在有错误时出现。
重试机制: 最多尝试 3 次。响应非 2xx 或请求出错时会重试,间隔依次为 100 毫秒、400 毫秒。每次请求超时为 30 秒。