HTTP API

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 状态含义
unauthorized401鉴权失败
invalid_request400请求体/参数非法
task_creation_failed400任务创建失败
task_not_found404任务 ID 不存在
cancel_failed500取消任务失败
internal_error500服务器内部错误

接口列表 #

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":  { }
}
字段类型必填说明
typestring是任务类型,见下文
storage_iduint是目标存储 ID
pathstring否存储内的子目录路径
webhookstring否任务完成/失败时的回调地址
paramsobject是各任务类型的专属参数,见下文

响应 201 Created:

{
  "task_id":      "abc123xyz",
  "type":         "directlinks",
  "status":       "queued",
  "storage_id":   1,
  "storage_name": "local",
  "created_at": "2026-03-11T10:00:00Z"
}

任务类型与 params #

下载一个或多个 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 条
optionsmap[string]string否Aria2 下载选项
parseditem — 解析器下载 #

将 URL 交由已注册的 JS 插件或内置解析器处理后下载。

{
  "type":    "parseditem",
  "storage_id": 1,
  "path":    "parsed",
  "params": {
    "url": "https://some-site.com/page"
  }
}
params 字段类型必填说明
urlstring是待解析的页面 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_idstring否与消息范围配合使用的 Bot API 聊天 ID 或用户名
message_rangestring否消息范围,例如 100-120
filterstring否可选正则,匹配消息文本或文件名
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_urlstring是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_iduint是源存储 ID
source_pathstring是源存储中的路径,须包含至少一个文件
target_storage_iduint是目标存储 ID
target_pathstring是目标存储中的路径

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_id
  • 404 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_id
  • 404 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_unavailable
  • 400 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 秒。