HTTP API

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_):

Environment VariableConfig Key
SAVEANY_API_ENABLEapi.enable
SAVEANY_API_HOSTapi.host
SAVEANY_API_PORTapi.port
SAVEANY_API_TOKENapi.token
If `token` is empty, the API server will be accessible **without any authentication**, which is a security risk.

Authentication #

When token is configured, all API requests must include a Bearer token in the HTTP header:

Authorization: Bearer <your-token>

On authentication failure, the server returns 401:

{ "error": "unauthorized", "message": "invalid token" }

Error Response Format #

All errors use a consistent JSON format:

{
  "error":   "error_code",
  "message": "human readable description"
}

Common error codes:

Error CodeHTTP StatusMeaning
unauthorized401Authentication failed
method_not_allowed405Wrong HTTP method
invalid_request400Malformed request body or parameters
task_creation_failed400Failed to create task
task_not_found404Task ID does not exist
cancel_failed500Failed to cancel task
internal_error500Internal server error

Endpoints #

GET /health — Health Check #

No authentication required.

Response 200 OK:

{ "status": "ok" }

GET /api/v1/storages — List Storages #

Returns all currently loaded storage backends.

Response 200 OK:

{
  "storages": [
    { "name": "local",   "type": "local" },
    { "name": "MyMinio", "type": "s3" }
  ]
}

GET /api/v1/task-types — List Supported Task Types #

Response 200 OK:

{
  "types": [
    "directlinks",
    "ytdlp",
    "aria2",
    "parseditem",
    "tgfiles",
    "tphpics",
    "transfer"
  ]
}

POST /api/v1/tasks — Create Task #

Request headers:

Content-Type: application/json
Authorization: Bearer <token>

Request body:

{
  "type":    "<task_type>",
  "storage": "<storage_name>",
  "path":    "<subpath>",
  "webhook": "<callback_url>",
  "params":  { }
}
FieldTypeRequiredDescription
typestringYesTask type — see below
storagestringYesTarget storage name, must match a name in your config
pathstringNoSubdirectory path within the storage
webhookstringNoCallback URL invoked when the task reaches a terminal state
paramsobjectYesType-specific parameters — see below

Response 201 Created:

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

Task Types and params #

Download one or more files from direct HTTP/HTTPS URLs.

{
  "type":    "directlinks",
  "storage": "local",
  "path":    "downloads",
  "params": {
    "urls": [
      "https://example.com/file.zip",
      "https://example.com/other.zip"
    ]
  }
}
params fieldTypeRequiredDescription
urls[]stringYesList of download URLs, at least 1
ytdlp — yt-dlp Media Download #
Requires yt-dlp to be installed on the system.

Download videos or audio via yt-dlp, supporting YouTube, Bilibili, and 1000+ other sites.

{
  "type":    "ytdlp",
  "storage": "local",
  "path":    "videos",
  "params": {
    "urls":  ["https://www.youtube.com/watch?v=xxx"],
    "flags": ["--extract-audio", "--audio-format", "mp3"]
  }
}
params fieldTypeRequiredDescription
urls[]stringYesList of media URLs, at least 1
flags[]stringNoExtra yt-dlp command-line flags
aria2 — Aria2 Download #
Requires Aria2 to be enabled and configured (RPC) in the config file.

Download files via the Aria2 download manager, supporting HTTP/HTTPS, FTP, BitTorrent (magnet links, torrent files), and more.

{
  "type":    "aria2",
  "storage": "local",
  "path":    "downloads",
  "params": {
    "urls":    ["magnet:?xt=urn:btih:..."],
    "options": { "split": "4" }
  }
}
params fieldTypeRequiredDescription
urls[]stringYesList of download URIs, at least 1
optionsmap[string]stringNoAria2 download options
parseditem — Parser Plugin Download #

Hand a URL off to a registered JS plugin or built-in parser for processing and downloading.

{
  "type":    "parseditem",
  "storage": "local",
  "path":    "parsed",
  "params": {
    "url": "https://some-site.com/page"
  }
}
params fieldTypeRequiredDescription
urlstringYesThe URL to parse

Returns 400 task_creation_failed if no parser is able to handle the URL.

tgfiles — Telegram Message File Download #

Download files from Telegram messages via message links. Supported link formats:

  • https://t.me/username/123 — public channel or group
  • https://t.me/c/123456789/123 — private channel by numeric ID
  • https://t.me/c/123456789/111/456 — topic message (thread ID / message ID)
  • https://t.me/username/111/456 — topic under a username-based chat

If the message is part of a media group (album), all files in the group are downloaded by default. Append ?single to the link to force downloading only the single specified message.

{
  "type":    "tgfiles",
  "storage": "local",
  "path":    "telegram",
  "params": {
    "message_links": [
      "https://t.me/username/123",
      "https://t.me/c/1234567890/456"
    ]
  }
}
params fieldTypeRequiredDescription
message_links[]stringYesList of Telegram message links, at least 1
tphpics — Telegraph Article Images #

Download all images from a Telegra.ph article.

Supported URL prefixes: https://telegra.ph/, http://telegra.ph/, https://telegraph.co/, http://telegraph.co/

{
  "type":    "tphpics",
  "storage": "local",
  "path":    "telegraph",
  "params": {
    "telegraph_url": "https://telegra.ph/Some-Article-01-01"
  }
}
params fieldTypeRequiredDescription
telegraph_urlstringYesURL of the Telegra.ph article
transfer — Storage-to-Storage Transfer #

Transfer files directly between two storage backends without going through Telegram. The source storage must support both listing and reading.

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`.
{
  "type":    "transfer",
  "storage": "local",
  "params": {
    "source_storage": "MyS3",
    "source_path":    "backups/",
    "target_storage": "LocalDisk",
    "target_path":    "restored/"
  }
}
params fieldTypeRequiredDescription
source_storagestringYesSource storage name
source_pathstringYesPath within the source storage; must contain at least one file
target_storagestringYesTarget storage name
target_pathstringYesDestination path within the target storage

GET /api/v1/tasks — List All Tasks #

Returns all tasks created via the API. Task records are stored in memory only and are cleared on restart.

Response 200 OK:

{
  "tasks": [
    {
      "task_id":    "abc123xyz",
      "type":       "directlinks",
      "status":     "running",
      "title":      "file.zip",
      "storage":    "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,
        "percent":          50.0
      }
    }
  ],
  "total": 1
}

The progress field is only included when total_bytes > 0. The error field is only included when non-empty.


GET /api/v1/tasks/{task_id} — Get Task #

Path parameter: task_id — the ID returned when the task was created.

Response 200 OK: Same structure as a single task object from the list above.

Error responses:

  • 400 invalid_request — no task ID in path
  • 404 task_not_found — task does not exist

DELETE /api/v1/tasks/{task_id} — Cancel Task #

Path parameter: task_id

Response 200 OK:

{ "message": "task cancelled successfully" }

Error responses:

  • 400 invalid_request — no task ID in path
  • 404 task_not_found — task does not exist
  • 500 cancel_failed — cancellation failed

Task Statuses #

StatusMeaning
queuedTask is queued and waiting to run
runningTask is currently executing
completedTask finished successfully
failedTask encountered an error
cancelledTask was cancelled via the DELETE endpoint

Webhook Callbacks #

When a webhook URL is provided in the create request, SaveAny-Bot sends a POST request to that URL when the task reaches a terminal state (completed, failed, or cancelled).

Callback request headers:

Content-Type: application/json
User-Agent: SaveAny-Bot/1.0

Callback request body:

{
  "task_id":      "abc123xyz",
  "type":         "directlinks",
  "status":       "completed",
  "storage":      "local",
  "path":         "downloads",
  "completed_at": "2026-03-11T10:01:00Z",
  "error":        ""
}

completed_at is only present when status is completed or failed. error is only present when non-empty.

Retry policy: Up to 3 attempts, with delays of 1s, 2s, and 3s between retries. Each request has a 30-second timeout.