188 lines
39 KiB
HTML
188 lines
39 KiB
HTML
<!doctype html><html lang=en dir=ltr><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content='
|
|
HTTP API
|
|
#
|
|
|
|
SaveAny-Bot provides an HTTP API that allows you to programmatically create download/transfer tasks, query task status, cancel tasks, and more — without going through Telegram.
|
|
|
|
Enabling the API
|
|
#
|
|
|
|
Add or modify the following section in config.toml:
|
|
[api]
|
|
enable = true
|
|
host = "0.0.0.0" # Bind address, default 0.0.0.0
|
|
port = 8080 # Listen port, default 8080
|
|
token = "your-token" # Auth token — strongly recommended
|
|
You can also override these settings with environment variables (prefix SAVEANY_):'><meta name=theme-color media="(prefers-color-scheme: light)" content="#ffffff"><meta name=theme-color media="(prefers-color-scheme: dark)" content="#343a40"><meta name=color-scheme content="light dark"><meta property="og:url" content="https://sabot.unv.app/en/usage/api/"><meta property="og:site_name" content="Save Any Bot"><meta property="og:title" content="HTTP API"><meta property="og:description" content='HTTP API # SaveAny-Bot provides an HTTP API that allows you to programmatically create download/transfer tasks, query task status, cancel tasks, and more — without going through Telegram.
|
|
Enabling the API # Add or modify the following section in config.toml:
|
|
[api] enable = true host = "0.0.0.0" # Bind address, default 0.0.0.0 port = 8080 # Listen port, default 8080 token = "your-token" # Auth token — strongly recommended You can also override these settings with environment variables (prefix SAVEANY_):'><meta property="og:locale" content="en"><meta property="og:type" content="article"><meta property="article:section" content="usage"><meta property="article:modified_time" content="2026-03-11T19:37:25+08:00"><title>HTTP API | Save Any Bot</title><link rel=icon href=/favicon.png><link rel=manifest href=/manifest.json><link rel=canonical href=https://sabot.unv.app/en/usage/api/><link rel=alternate hreflang=zh href=https://sabot.unv.app/usage/api/ title="HTTP API"><link rel=stylesheet href=/book.min.a22f4c7d8c2bdc5e3d6e34ba11cb59ab50ea5772594e71305bfd5a595dc78b7e.css integrity="sha256-oi9MfYwr3F49bjS6EctZq1DqV3JZTnEwW/1aWV3Hi34=" crossorigin=anonymous></head><body dir=ltr><input type=checkbox class="hidden toggle" id=menu-control>
|
|
<input type=checkbox class="hidden toggle" id=toc-control><main class="container flex"><aside class=book-menu><div class=book-menu-content><nav><h2 class=book-brand><a class="flex align-center" href=/en/><img src=/logo.png alt=Logo class=book-icon><span>Save Any Bot</span></a></h2><ul class=book-languages><li><input type=checkbox id=languages class=toggle>
|
|
<label for=languages class=flex><a role=button class="flex flex-auto"><img src=/svg/translate.svg class=book-icon alt=Languages>
|
|
English</a></label><ul><li><a href=/usage/api/>简体中文</a></li></ul></li></ul><ul><li><a href=https://github.com/krau/SaveAny-Bot target=_blank rel=noopener>🔗 GitHub</a></li></ul><ul><li><span>Deployment Guide</span><ul><li><a href=/en/deployment/configuration/>Configuration Guide</a><ul><li><a href=/en/deployment/configuration/storages/>Storage Configuration</a></li></ul></li><li><a href=/en/deployment/installation/>Installation and Updates</a></li></ul></li><li><a href=/en/usage/>Usage</a><ul><li><a href=/en/usage/silent/>Silent Mode</a></li><li><a href=/en/usage/rules/>Storage Rules</a></li><li><a href=/en/usage/watch/>Watch Chats</a></li><li><a href=/en/usage/directlinks/>Direct Download Links</a></li><li><a href=/en/usage/aria2/>Aria2 Download</a></li><li><a href=/en/usage/ytdlp/>yt-dlp Video Download</a></li><li><a href=/en/usage/transfer/>Storage Transfer</a></li><li><a href=/en/usage/parsers/>Save Files Outside Telegram</a></li><li><a href=/en/usage/api/ class=active>HTTP API</a></li></ul></li><li><a href=/en/help/>Frequently Asked Questions</a><ul></ul></li><li><a href=/en/contribute/>Contributing</a><ul></ul></li></ul></nav><script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script></div></aside><div class=book-page><header class=book-header><div class="flex align-center justify-between"><label for=menu-control><img src=/svg/menu.svg class=book-icon alt=Menu></label><h3>HTTP API</h3><label for=toc-control><img src=/svg/toc.svg class=book-icon alt="Table of Contents"></label></div><aside class="hidden clearfix"><nav id=TableOfContents><ul><li><a href=#enabling-the-api>Enabling the API</a></li><li><a href=#authentication>Authentication</a></li><li><a href=#error-response-format>Error Response Format</a></li><li><a href=#endpoints>Endpoints</a><ul><li><a href=#get-health--health-check>GET /health — Health Check</a></li><li><a href=#get-apiv1storages--list-storages>GET /api/v1/storages — List Storages</a></li><li><a href=#get-apiv1task-types--list-supported-task-types>GET /api/v1/task-types — List Supported Task Types</a></li><li><a href=#post-apiv1tasks--create-task>POST /api/v1/tasks — Create Task</a></li><li><a href=#get-apiv1tasks--list-all-tasks>GET /api/v1/tasks — List All Tasks</a></li><li><a href=#get-apiv1taskstask_id--get-task>GET /api/v1/tasks/{task_id} — Get Task</a></li><li><a href=#delete-apiv1taskstask_id--cancel-task>DELETE /api/v1/tasks/{task_id} — Cancel Task</a></li></ul></li><li><a href=#task-statuses>Task Statuses</a></li><li><a href=#webhook-callbacks>Webhook Callbacks</a></li></ul></nav></aside></header><article class="markdown book-article"><h1 id=http-api>HTTP API
|
|
<a class=anchor href=#http-api>#</a></h1><p>SaveAny-Bot provides an HTTP API that allows you to programmatically create download/transfer tasks, query task status, cancel tasks, and more — without going through Telegram.</p><h2 id=enabling-the-api>Enabling the API
|
|
<a class=anchor href=#enabling-the-api>#</a></h2><p>Add or modify the following section in <code>config.toml</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-toml data-lang=toml><span style=display:flex><span>[<span style=color:#a6e22e>api</span>]
|
|
</span></span><span style=display:flex><span><span style=color:#a6e22e>enable</span> = <span style=color:#66d9ef>true</span>
|
|
</span></span><span style=display:flex><span><span style=color:#a6e22e>host</span> = <span style=color:#e6db74>"0.0.0.0"</span> <span style=color:#75715e># Bind address, default 0.0.0.0</span>
|
|
</span></span><span style=display:flex><span><span style=color:#a6e22e>port</span> = <span style=color:#ae81ff>8080</span> <span style=color:#75715e># Listen port, default 8080</span>
|
|
</span></span><span style=display:flex><span><span style=color:#a6e22e>token</span> = <span style=color:#e6db74>"your-token"</span> <span style=color:#75715e># Auth token — strongly recommended</span>
|
|
</span></span></code></pre></div><p>You can also override these settings with environment variables (prefix <code>SAVEANY_</code>):</p><table><thead><tr><th>Environment Variable</th><th>Config Key</th></tr></thead><tbody><tr><td><code>SAVEANY_API_ENABLE</code></td><td><code>api.enable</code></td></tr><tr><td><code>SAVEANY_API_HOST</code></td><td><code>api.host</code></td></tr><tr><td><code>SAVEANY_API_PORT</code></td><td><code>api.port</code></td></tr><tr><td><code>SAVEANY_API_TOKEN</code></td><td><code>api.token</code></td></tr></tbody></table><blockquote class="book-hint warning">If `token` is empty, the API server will be accessible **without any authentication**, which is a security risk.</blockquote><h2 id=authentication>Authentication
|
|
<a class=anchor href=#authentication>#</a></h2><p>When <code>token</code> is configured, all API requests must include a Bearer token in the HTTP header:</p><pre tabindex=0><code>Authorization: Bearer <your-token>
|
|
</code></pre><p>On authentication failure, the server returns <code>401</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ <span style=color:#f92672>"error"</span>: <span style=color:#e6db74>"unauthorized"</span>, <span style=color:#f92672>"message"</span>: <span style=color:#e6db74>"invalid token"</span> }
|
|
</span></span></code></pre></div><h2 id=error-response-format>Error Response Format
|
|
<a class=anchor href=#error-response-format>#</a></h2><p>All errors use a consistent JSON format:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"error"</span>: <span style=color:#e6db74>"error_code"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"message"</span>: <span style=color:#e6db74>"human readable description"</span>
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><p>Common error codes:</p><table><thead><tr><th>Error Code</th><th>HTTP Status</th><th>Meaning</th></tr></thead><tbody><tr><td><code>unauthorized</code></td><td>401</td><td>Authentication failed</td></tr><tr><td><code>method_not_allowed</code></td><td>405</td><td>Wrong HTTP method</td></tr><tr><td><code>invalid_request</code></td><td>400</td><td>Malformed request body or parameters</td></tr><tr><td><code>task_creation_failed</code></td><td>400</td><td>Failed to create task</td></tr><tr><td><code>task_not_found</code></td><td>404</td><td>Task ID does not exist</td></tr><tr><td><code>cancel_failed</code></td><td>500</td><td>Failed to cancel task</td></tr><tr><td><code>internal_error</code></td><td>500</td><td>Internal server error</td></tr></tbody></table><hr><h2 id=endpoints>Endpoints
|
|
<a class=anchor href=#endpoints>#</a></h2><h3 id=get-health--health-check>GET /health — Health Check
|
|
<a class=anchor href=#get-health--health-check>#</a></h3><p>No authentication required.</p><p><strong>Response <code>200 OK</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ <span style=color:#f92672>"status"</span>: <span style=color:#e6db74>"ok"</span> }
|
|
</span></span></code></pre></div><hr><h3 id=get-apiv1storages--list-storages>GET /api/v1/storages — List Storages
|
|
<a class=anchor href=#get-apiv1storages--list-storages>#</a></h3><p>Returns all currently loaded storage backends.</p><p><strong>Response <code>200 OK</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storages"</span>: [
|
|
</span></span><span style=display:flex><span> { <span style=color:#f92672>"name"</span>: <span style=color:#e6db74>"local"</span>, <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"local"</span> },
|
|
</span></span><span style=display:flex><span> { <span style=color:#f92672>"name"</span>: <span style=color:#e6db74>"MyMinio"</span>, <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"s3"</span> }
|
|
</span></span><span style=display:flex><span> ]
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><hr><h3 id=get-apiv1task-types--list-supported-task-types>GET /api/v1/task-types — List Supported Task Types
|
|
<a class=anchor href=#get-apiv1task-types--list-supported-task-types>#</a></h3><p><strong>Response <code>200 OK</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"types"</span>: [
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"directlinks"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"ytdlp"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"aria2"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"parseditem"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"tgfiles"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"tphpics"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"transfer"</span>
|
|
</span></span><span style=display:flex><span> ]
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><hr><h3 id=post-apiv1tasks--create-task>POST /api/v1/tasks — Create Task
|
|
<a class=anchor href=#post-apiv1tasks--create-task>#</a></h3><p><strong>Request headers:</strong></p><pre tabindex=0><code>Content-Type: application/json
|
|
Authorization: Bearer <token>
|
|
</code></pre><p><strong>Request body:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"<task_type>"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"<storage_name>"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"<subpath>"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"webhook"</span>: <span style=color:#e6db74>"<callback_url>"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: { }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Yes</td><td>Task type — see below</td></tr><tr><td><code>storage</code></td><td>string</td><td>Yes</td><td>Target storage name, must match a name in your config</td></tr><tr><td><code>path</code></td><td>string</td><td>No</td><td>Subdirectory path within the storage</td></tr><tr><td><code>webhook</code></td><td>string</td><td>No</td><td>Callback URL invoked when the task reaches a terminal state</td></tr><tr><td><code>params</code></td><td>object</td><td>Yes</td><td>Type-specific parameters — see below</td></tr></tbody></table><p><strong>Response <code>201 Created</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"task_id"</span>: <span style=color:#e6db74>"abc123xyz"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"directlinks"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"status"</span>: <span style=color:#e6db74>"queued"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"created_at"</span>: <span style=color:#e6db74>"2026-03-11T10:00:00Z"</span>
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><h4 id=task-types-and-params>Task Types and params
|
|
<a class=anchor href=#task-types-and-params>#</a></h4><h5 id=directlinks--direct-url-download>directlinks — Direct URL Download
|
|
<a class=anchor href=#directlinks--direct-url-download>#</a></h5><p>Download one or more files from direct HTTP/HTTPS URLs.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"directlinks"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"downloads"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"urls"</span>: [
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"https://example.com/file.zip"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"https://example.com/other.zip"</span>
|
|
</span></span><span style=display:flex><span> ]
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>urls</code></td><td>[]string</td><td>Yes</td><td>List of download URLs, at least 1</td></tr></tbody></table><h5 id=ytdlp--yt-dlp-media-download>ytdlp — yt-dlp Media Download
|
|
<a class=anchor href=#ytdlp--yt-dlp-media-download>#</a></h5><blockquote class="book-hint warning">Requires yt-dlp to be installed on the system.</blockquote><p>Download videos or audio via yt-dlp, supporting YouTube, Bilibili, and 1000+ other sites.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"ytdlp"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"videos"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"urls"</span>: [<span style=color:#e6db74>"https://www.youtube.com/watch?v=xxx"</span>],
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"flags"</span>: [<span style=color:#e6db74>"--extract-audio"</span>, <span style=color:#e6db74>"--audio-format"</span>, <span style=color:#e6db74>"mp3"</span>]
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>urls</code></td><td>[]string</td><td>Yes</td><td>List of media URLs, at least 1</td></tr><tr><td><code>flags</code></td><td>[]string</td><td>No</td><td>Extra yt-dlp command-line flags</td></tr></tbody></table><h5 id=aria2--aria2-download>aria2 — Aria2 Download
|
|
<a class=anchor href=#aria2--aria2-download>#</a></h5><blockquote class="book-hint warning">Requires Aria2 to be enabled and configured (RPC) in the config file.</blockquote><p>Download files via the Aria2 download manager, supporting HTTP/HTTPS, FTP, BitTorrent (magnet links, torrent files), and more.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"aria2"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"downloads"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"urls"</span>: [<span style=color:#e6db74>"magnet:?xt=urn:btih:..."</span>],
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"options"</span>: { <span style=color:#f92672>"split"</span>: <span style=color:#e6db74>"4"</span> }
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>urls</code></td><td>[]string</td><td>Yes</td><td>List of download URIs, at least 1</td></tr><tr><td><code>options</code></td><td>map[string]string</td><td>No</td><td>Aria2 download options</td></tr></tbody></table><h5 id=parseditem--parser-plugin-download>parseditem — Parser Plugin Download
|
|
<a class=anchor href=#parseditem--parser-plugin-download>#</a></h5><p>Hand a URL off to a registered JS plugin or built-in parser for processing and downloading.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"parseditem"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"parsed"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"url"</span>: <span style=color:#e6db74>"https://some-site.com/page"</span>
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>url</code></td><td>string</td><td>Yes</td><td>The URL to parse</td></tr></tbody></table><p>Returns <code>400 task_creation_failed</code> if no parser is able to handle the URL.</p><h5 id=tgfiles--telegram-message-file-download>tgfiles — Telegram Message File Download
|
|
<a class=anchor href=#tgfiles--telegram-message-file-download>#</a></h5><p>Download files from Telegram messages via message links. Supported link formats:</p><ul><li><code>https://t.me/username/123</code> — public channel or group</li><li><code>https://t.me/c/123456789/123</code> — private channel by numeric ID</li><li><code>https://t.me/c/123456789/111/456</code> — topic message (thread ID / message ID)</li><li><code>https://t.me/username/111/456</code> — topic under a username-based chat</li></ul><p>If the message is part of a media group (album), all files in the group are downloaded by default. Append <code>?single</code> to the link to force downloading only the single specified message.</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"tgfiles"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"telegram"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"message_links"</span>: [
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"https://t.me/username/123"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#e6db74>"https://t.me/c/1234567890/456"</span>
|
|
</span></span><span style=display:flex><span> ]
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>message_links</code></td><td>[]string</td><td>Yes</td><td>List of Telegram message links, at least 1</td></tr></tbody></table><h5 id=tphpics--telegraph-article-images>tphpics — Telegraph Article Images
|
|
<a class=anchor href=#tphpics--telegraph-article-images>#</a></h5><p>Download all images from a Telegra.ph article.</p><p>Supported URL prefixes: <code>https://telegra.ph/</code>, <code>http://telegra.ph/</code>, <code>https://telegraph.co/</code>, <code>http://telegraph.co/</code></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"tphpics"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"telegraph"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"telegraph_url"</span>: <span style=color:#e6db74>"https://telegra.ph/Some-Article-01-01"</span>
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>telegraph_url</code></td><td>string</td><td>Yes</td><td>URL of the Telegra.ph article</td></tr></tbody></table><h5 id=transfer--storage-to-storage-transfer>transfer — Storage-to-Storage Transfer
|
|
<a class=anchor href=#transfer--storage-to-storage-transfer>#</a></h5><p>Transfer files directly between two storage backends without going through Telegram. The source storage must support both listing and reading.</p><blockquote class="book-hint info">For `transfer` tasks, the top-level `storage` field is still required for validation, but the actual storages used are determined by `source_storage` and `target_storage` inside `params`.</blockquote><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"transfer"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"params"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"source_storage"</span>: <span style=color:#e6db74>"MyS3"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"source_path"</span>: <span style=color:#e6db74>"backups/"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"target_storage"</span>: <span style=color:#e6db74>"LocalDisk"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"target_path"</span>: <span style=color:#e6db74>"restored/"</span>
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><table><thead><tr><th>params field</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>source_storage</code></td><td>string</td><td>Yes</td><td>Source storage name</td></tr><tr><td><code>source_path</code></td><td>string</td><td>Yes</td><td>Path within the source storage; must contain at least one file</td></tr><tr><td><code>target_storage</code></td><td>string</td><td>Yes</td><td>Target storage name</td></tr><tr><td><code>target_path</code></td><td>string</td><td>Yes</td><td>Destination path within the target storage</td></tr></tbody></table><hr><h3 id=get-apiv1tasks--list-all-tasks>GET /api/v1/tasks — List All Tasks
|
|
<a class=anchor href=#get-apiv1tasks--list-all-tasks>#</a></h3><p>Returns all tasks created via the API. Task records are stored in memory only and are cleared on restart.</p><p><strong>Response <code>200 OK</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"tasks"</span>: [
|
|
</span></span><span style=display:flex><span> {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"task_id"</span>: <span style=color:#e6db74>"abc123xyz"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"directlinks"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"status"</span>: <span style=color:#e6db74>"running"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"title"</span>: <span style=color:#e6db74>"file.zip"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"downloads"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"error"</span>: <span style=color:#e6db74>""</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"created_at"</span>: <span style=color:#e6db74>"2026-03-11T10:00:00Z"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"updated_at"</span>: <span style=color:#e6db74>"2026-03-11T10:00:05Z"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"progress"</span>: {
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"total_bytes"</span>: <span style=color:#ae81ff>10485760</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"downloaded_bytes"</span>: <span style=color:#ae81ff>5242880</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"percent"</span>: <span style=color:#ae81ff>50.0</span>
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span> }
|
|
</span></span><span style=display:flex><span> ],
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"total"</span>: <span style=color:#ae81ff>1</span>
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><p>The <code>progress</code> field is only included when <code>total_bytes > 0</code>. The <code>error</code> field is only included when non-empty.</p><hr><h3 id=get-apiv1taskstask_id--get-task>GET /api/v1/tasks/{task_id} — Get Task
|
|
<a class=anchor href=#get-apiv1taskstask_id--get-task>#</a></h3><p><strong>Path parameter:</strong> <code>task_id</code> — the ID returned when the task was created.</p><p><strong>Response <code>200 OK</code>:</strong> Same structure as a single task object from the list above.</p><p><strong>Error responses:</strong></p><ul><li><code>400 invalid_request</code> — no task ID in path</li><li><code>404 task_not_found</code> — task does not exist</li></ul><hr><h3 id=delete-apiv1taskstask_id--cancel-task>DELETE /api/v1/tasks/{task_id} — Cancel Task
|
|
<a class=anchor href=#delete-apiv1taskstask_id--cancel-task>#</a></h3><p><strong>Path parameter:</strong> <code>task_id</code></p><p><strong>Response <code>200 OK</code>:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{ <span style=color:#f92672>"message"</span>: <span style=color:#e6db74>"task cancelled successfully"</span> }
|
|
</span></span></code></pre></div><p><strong>Error responses:</strong></p><ul><li><code>400 invalid_request</code> — no task ID in path</li><li><code>404 task_not_found</code> — task does not exist</li><li><code>500 cancel_failed</code> — cancellation failed</li></ul><hr><h2 id=task-statuses>Task Statuses
|
|
<a class=anchor href=#task-statuses>#</a></h2><table><thead><tr><th>Status</th><th>Meaning</th></tr></thead><tbody><tr><td><code>queued</code></td><td>Task is queued and waiting to run</td></tr><tr><td><code>running</code></td><td>Task is currently executing</td></tr><tr><td><code>completed</code></td><td>Task finished successfully</td></tr><tr><td><code>failed</code></td><td>Task encountered an error</td></tr><tr><td><code>cancelled</code></td><td>Task was cancelled via the DELETE endpoint</td></tr></tbody></table><hr><h2 id=webhook-callbacks>Webhook Callbacks
|
|
<a class=anchor href=#webhook-callbacks>#</a></h2><p>When a <code>webhook</code> URL is provided in the create request, SaveAny-Bot sends a <code>POST</code> request to that URL when the task reaches a terminal state (<code>completed</code>, <code>failed</code>, or <code>cancelled</code>).</p><p><strong>Callback request headers:</strong></p><pre tabindex=0><code>Content-Type: application/json
|
|
User-Agent: SaveAny-Bot/1.0
|
|
</code></pre><p><strong>Callback request body:</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-json data-lang=json><span style=display:flex><span>{
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"task_id"</span>: <span style=color:#e6db74>"abc123xyz"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"type"</span>: <span style=color:#e6db74>"directlinks"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"status"</span>: <span style=color:#e6db74>"completed"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"storage"</span>: <span style=color:#e6db74>"local"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"path"</span>: <span style=color:#e6db74>"downloads"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"completed_at"</span>: <span style=color:#e6db74>"2026-03-11T10:01:00Z"</span>,
|
|
</span></span><span style=display:flex><span> <span style=color:#f92672>"error"</span>: <span style=color:#e6db74>""</span>
|
|
</span></span><span style=display:flex><span>}
|
|
</span></span></code></pre></div><p><code>completed_at</code> is only present when status is <code>completed</code> or <code>failed</code>. <code>error</code> is only present when non-empty.</p><p><strong>Retry policy:</strong> Up to 3 attempts, with delays of 1s, 2s, and 3s between retries. Each request has a 30-second timeout.</p></article><footer class=book-footer><div class="flex flex-wrap justify-between"><div><a class="flex align-center" href=https://github.com/krau/saveany-bot/commit/38355dfd142f0f1a819a8837875b33da0d3a81b7 title='Last modified by krau | 2026/03/11' target=_blank rel=noopener><img src=/svg/calendar.svg class=book-icon alt>
|
|
<span>2026/03/11</span></a></div><div><a class="flex align-center" href=https://github.com/krau/saveany-bot/edit/main/docs/content/en/usage/api.md target=_blank rel=noopener><img src=/svg/edit.svg class=book-icon alt>
|
|
<span>Edit this page</span></a></div></div><script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script></footer><div class=book-comments></div><label for=menu-control class="hidden book-menu-overlay"></label></div><aside class=book-toc><div class=book-toc-content><nav id=TableOfContents><ul><li><a href=#enabling-the-api>Enabling the API</a></li><li><a href=#authentication>Authentication</a></li><li><a href=#error-response-format>Error Response Format</a></li><li><a href=#endpoints>Endpoints</a><ul><li><a href=#get-health--health-check>GET /health — Health Check</a></li><li><a href=#get-apiv1storages--list-storages>GET /api/v1/storages — List Storages</a></li><li><a href=#get-apiv1task-types--list-supported-task-types>GET /api/v1/task-types — List Supported Task Types</a></li><li><a href=#post-apiv1tasks--create-task>POST /api/v1/tasks — Create Task</a></li><li><a href=#get-apiv1tasks--list-all-tasks>GET /api/v1/tasks — List All Tasks</a></li><li><a href=#get-apiv1taskstask_id--get-task>GET /api/v1/tasks/{task_id} — Get Task</a></li><li><a href=#delete-apiv1taskstask_id--cancel-task>DELETE /api/v1/tasks/{task_id} — Cancel Task</a></li></ul></li><li><a href=#task-statuses>Task Statuses</a></li><li><a href=#webhook-callbacks>Webhook Callbacks</a></li></ul></nav></div></aside></main></body></html> |