Two contenders, two philosophies. Pick your fighter:
| Tool | Vibe | Best For |
|---|---|---|
| qBittorrent | Full-service daemon with REST API | Managing many torrents, categories, trackers, automation |
| aria2c | Lightweight single-binary wildcard | Quick downloads, HTTP/FTP/BT multifetch, minimal setup |
Platform notes
- Linux server/headless flow: excellent for
qbittorrent-nox - Windows desktop flow: fully viable for personal-machine use, especially qBittorrent portable mode or
aria2c - Validate connectivity and storage path permissions before assuming the client is broken
Installation Check
qBittorrent
# Check if qbittorrent-nox daemon is available
Get-Command qbittorrent-nox -ErrorAction SilentlyContinue
# Check for the Python API client (preferred for automation)
python -c "import qbittorrentapi" 2>$null
If not installed:
# Windows: Install via Chocolatey
choco install qbittorrent
# Portable mode: extract from installer with 7-Zip, create 'profile' folder
# (see "Portable Mode" section below — no system install needed!)
# Or grab the Python API client (no daemon needed if connecting to remote)
uv pip install qbittorrent-api
Note: qBittorrent runs as a background daemon (
qbittorrent-nox). The Web API listens on port 8080 by default. The Python client can talk to it locally or remotely.qBittorrent v5.x support: The
qbittorrent-apiPython package v2026.5.1 supports qBittorrent v5.2.0 (Web API v2.15.1, released May 2026) — fully compatible. Cookie management, web seeds, API key auth — all covered.
aria2c
# Already installed? Check:
aria2c --version
If not installed:
# Windows (Chocolatey):
choco install aria2
# Or download the binary from:
# https://github.com/aria2/aria2/releases
aria2c is a single binary — no daemon needed for basic use. Just point it at a URI and it goes. For persistent management, enable the RPC mode.
qBittorrent — Full-Service Torrent Manager
Architecture
┌─────────────────┐ HTTP API (8080) ┌──────────────┐
│ qbittorrent-nox │ ◄─────────────────────► │ Your Code │
│ (daemon process) │ │ (curl/Python)│
└─────────────────┘ └──────────────┘
qBittorrent's Web API is at /api/v2/ — all endpoints require authentication (except login).
Official Documentation
| Resource | Link |
|---|---|
| Wiki Home | github.com/qbittorrent/qBittorrent/wiki |
| WebUI API (v5.0+) | WebUI API (qBittorrent 5.0) |
| WebUI API (v4.1 - v4.6.x) | WebUI API (qBittorrent 4.1) |
| Portable Mode | How to use portable mode |
| API Key Auth (≥v5.2.0) | API Key Authentication |
| Python Client | qbittorrent-api on Read the Docs |
1. Start the Daemon
# Start in background (headless)
qbittorrent-nox --webui-port=8080
# Or run as a background job with the bg skill
bg run "qbittorrent-nox --webui-port=8080"
Default WebUI credentials: admin / adminadmin
Note on v4.6.1+: Newer versions generate a one-time password on first launch instead of using
adminadmin. Check the daemon's console output for the temporary password.
🔄 Portable Mode (No System Install)
qBittorrent supports portable mode (v4.2.1+) — all configs live in a local profile
folder, so no system files are touched and the whole thing runs from a USB stick or
any directory. Works on both Windows and Linux.
Windows:
- Extract
qbittorrent.exeandqbittorrent.pdbfrom the installer using 7-Zip (installers are just self-extracting archives). - Create a folder called
profilenext to the EXE. - Run
qbittorrent.exe— it initializes config files insideprofile/automatically. - Move the whole folder anywhere, including to another machine. Configs travel with it.
Linux:
# Point to a portable config directory
qbittorrent-nox --profile=/path/to/portable/config --relative-fastresume
# Or create a 'profile' folder next to the binary (same as Windows behavior)
How it interacts with the API: Portable or not — the Web API is identical.
The --webui-port flag still works, the Python client doesn't care where configs
live. You can run portable mode on a headless server:
bg run "qbittorrent-nox --profile=D:/qbt-portable --webui-port=8080"
Multiple instances via --configuration=NAME:
# Each gets its own config directory (~/.config/qbittorrent_NAME)
qbittorrent-nox --configuration=media
qbittorrent-nox --configuration=games
2. Authentication (curl)
# Login and capture the SID cookie
curl -c cookies.txt -X POST \
--header 'Referer: http://localhost:8080' \
--data 'username=admin&password=adminadmin' \
http://localhost:8080/api/v2/auth/login
# Use the cookie for authenticated requests
curl -b cookies.txt http://localhost:8080/api/v2/torrents/info
Important: The
Refererheader must match the host:port exactly, otherwise qBittorrent drops the request.
3. Python Client (Preferred for Automation)
# Quick one-shot:
uv run --with qbittorrent-api python -c "
import qbittorrentapi
qbt = qbittorrentapi.Client(
host='localhost:8080',
username='admin',
password='adminadmin'
)
# This will auto-login and handle cookie refresh
print(f'qBittorrent: {qbt.app.version}')
print(f'Web API: {qbt.app.web_api_version}')
# List all torrents
for t in qbt.torrents_info():
print(f'{t.hash[-8:]}: {t.name} ({t.state}) [{t.progress*100:.1f}%]')
"
Or use it as a managed script:
import qbittorrentapi
from pathlib import Path
conn_info = dict(
host="localhost",
port=8080,
username="admin",
password="adminadmin",
)
with qbittorrentapi.Client(**conn_info) as qbt:
# --- Add torrents ---
# From URL
qbt.torrents_add(urls="magnet:?xt=urn:btih:...")
# From local .torrent file
qbt.torrents_add(torrent_files=[str(Path("file.torrent"))])
# From torrent file data (bytes)
qbt.torrents_add(torrent_files=[torrent_bytes])
# With options
qbt.torrents_add(
urls="magnet:...",
save_path="D:/Downloads",
category="movies",
tags=["4k", "hdr"],
paused=True,
ratio_limit=2.0,
seeding_time_limit=1440, # minutes
)
# --- List torrents (with filtering) ---
all_torrents = qbt.torrents_info()
downloading = qbt.torrents_info(filter="downloading")
completed = qbt.torrents_info(filter="completed")
active = qbt.torrents_info(filter="active")
inactive = qbt.torrents_info(filter="inactive")
stalled = qbt.torrents_info(filter="stalled_downloading")
tagged = qbt.torrents_info(tag="4k")
# --- Control torrents ---
hashes = [t.hash for t in all_torrents]
# Pause / Resume
qbt.torrents_pause(torrent_hashes=hashes)
qbt.torrents_resume(torrent_hashes=hashes)
# Delete (also remove files from disk)
qbt.torrents_delete(delete_files=True, torrent_hashes=hashes)
# Recheck
qbt.torrents_recheck(torrent_hashes=hashes)
# Reannounce
qbt.torrents_reannounce(torrent_hashes=hashes)
# Set force start
qbt.torrents_set_force_start(torrent_hashes=hashes)
# --- Trackers ---
torrent_hash = all_torrents[0].hash
# Get trackers for a torrent
trackers = qbt.torrents_trackers(torrent_hash=torrent_hash)
for tr in trackers:
print(f'{tr.url}: {tr.status} (seeds: {tr.num_seeds})')
# Add trackers
qbt.torrents_add_trackers(
torrent_hash=torrent_hash,
urls="udp://tracker.opentrackr.org:1337\nudp://tracker.coppersurfer.tk:6969"
)
# Edit tracker
qbt.torrents_edit_tracker(
torrent_hash=torrent_hash,
orig_url="http://old-tracker.com/announce",
new_url="http://new-tracker.com/announce"
)
# Remove trackers
qbt.torrents_remove_trackers(
torrent_hash=torrent_hash,
urls="http://dead-tracker.com/announce"
)
# --- Categories ---
# List all categories
cats = qbt.torrents_categories()
# Create category
qbt.torrents_create_category(category="movies", save_path="D:/Torrents/Movies")
# Edit category
qbt.torrents_edit_category(category="movies", save_path="D:/Media/Movies")
# Remove categories
qbt.torrents_remove_categories(categories="movies")
# Set torrent category
qbt.torrents_set_category(torrent_hashes=hashes, category="movies")
# --- Tags ---
qbt.torrents_create_tags(tags="4k,hdr,remux")
qbt.torrents_add_tags(torrent_hashes=hashes, tags="4k")
qbt.torrents_remove_tags(torrent_hashes=hashes, tags="4k")
qbt.torrents_delete_tags(tags="old-tag")
# --- Speed Limits ---
# Per-torrent (bytes/sec)
qbt.torrents_set_download_limit(torrent_hashes=hashes, limit=0) # 0 = unlimited
qbt.torrents_set_upload_limit(torrent_hashes=hashes, limit=102400) # 100 KiB/s
# Global
qbt.transfer_set_download_limit(limit=0)
qbt.transfer_set_upload_limit(limit=512000) # 500 KiB/s
# Toggle alt speed limits
qbt.transfer_toggle_speed_limits_mode()
# --- Share Limits ---
qbt.torrents_set_share_limits(
torrent_hashes=hashes,
ratio_limit=2.0, # stop seeding at ratio 2.0
seeding_time_limit=1440 # or after 1440 minutes
)
# --- Priority ---
qbt.torrents_increase_priority(torrent_hashes=hashes)
qbt.torrents_decrease_priority(torrent_hashes=hashes)
qbt.torrents_top_priority(torrent_hashes=hashes)
qbt.torrents_bottom_priority(torrent_hashes=hashes)
# --- Files within torrent ---
files = qbt.torrents_files(torrent_hash=torrent_hash)
for f in files:
print(f'{f.index}: {f.name} ({f.size} bytes, priority={f.priority})')
# Set file priority (0=skip, 1=normal, 6=high, 7=max)
qbt.torrents_file_priority(
torrent_hash=torrent_hash,
file_ids=[0, 1], # file indices
priority=0 # skip these files
)
# --- Rename ---
qbt.torrents_rename(torrent_hash=torrent_hash, new_name="New Name")
qbt.torrents_rename_file(torrent_hash=torrent_hash, old_path="ep1.mkv", new_path="Episode_1.mkv")
qbt.torrents_rename_folder(torrent_hash=torrent_hash, old_path="Season1", new_path="Season_01")
# --- Torrent Properties ---
props = qbt.torrents_properties(torrent_hash=torrent_hash)
print(f'Save path: {props.save_path}')
print(f'Total size: {props.total_size}')
print(f'Added on: {props.addition_date}')
print(f'Downloaded: {props.total_downloaded}')
print(f'Ratio: {props.ratio}')
print(f'Seeding time: {props.seeding_time}s')
🆕 qBittorrent v5.x — What Changed
qBittorrent v5.0+ uses Web API v2.9.3+. The latest release v5.2.0 (May 3, 2026) ships Web API v2.15.1. Most v4.x API endpoints remain the same, but there are additions and removals:
| Change | Version | Detail |
|---|---|---|
reannounce field in /torrents/info |
API v2.9.3 | Each torrent now reports reannounce timestamp |
| Cookie management APIs | API v2.11.3 | New app/cookies (GET) and app/setCookies (POST) |
cookie field removed from /torrents/add |
API v2.11.3 | Use the cookie management APIs instead |
| API Key Authentication | ≥v5.2.0 | New auth method — generate API keys for script access instead of sharing passwords. See wiki. |
| WebSeed management endpoints | Added | torrents/addWebSeeds, torrents/editWebSeed, torrents/removeWebSeeds |
| Tracker status additions | v5.2.0 | New tracker status values |
Python client compatibility: The qbittorrent-api package v2026.5.1 supports
qBittorrent v5.2.0 (Web API v2.15.1) — so yes, the Python client fully covers
v5.x including the new cookie and web seed endpoints. Install/upgrade with:
uv pip install --upgrade qbittorrent-api
4. curl Cheat Sheet (if Python isn't available)
# Variables
SID="SID=your_cookie_here"
BASE="http://localhost:8080/api/v2"
# Login
SID=$(curl -s -c - -X POST \
--header 'Referer: http://localhost:8080' \
--data 'username=admin&password=adminadmin' \
"$BASE/auth/login" | grep SID | awk '{print $NF}')
# List torrents
curl -s --cookie "$SID" "$BASE/torrents/info"
# Filter by state
curl -s --cookie "$SID" "$BASE/torrents/info?filter=downloading"
# Add torrent from URL (magnet or http)
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'urls=magnet:?...' \
"$BASE/torrents/add"
# Add torrent with category & save path
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
-F 'torrents=@file.torrent' \
-F 'category=movies' \
-F 'savepath=D:/Downloads' \
-F 'paused=false' \
"$BASE/torrents/add"
# Pause all
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'hashes=all' \
"$BASE/torrents/pause"
# Resume all
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'hashes=all' \
"$BASE/torrents/resume"
# Delete with files
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'hashes=all&deleteFiles=true' \
"$BASE/torrents/delete"
# Get global transfer info (speed, etc.)
curl -s --cookie "$SID" "$BASE/transfer/info"
# Set global download limit (bytes/sec)
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'limit=1048576' \
"$BASE/transfer/setDownloadLimit"
# Get application preferences
curl -s --cookie "$SID" "$BASE/app/preferences"
# Set preference (e.g., enable DHT)
curl -s -X POST --cookie "$SID" \
--header 'Referer: http://localhost:8080' \
--data 'json={"dht":true}' \
"$BASE/app/setPreferences"
5. Key qBittorrent API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v2/auth/login |
Login, get SID cookie |
| POST | /api/v2/auth/logout |
Logout |
| GET | /api/v2/app/version |
App version string |
| GET | /api/v2/app/webapiVersion |
API version string |
| GET | /api/v2/app/buildInfo |
Build info (Qt, libtorrent, etc.) |
| POST | /api/v2/app/shutdown |
Stop the daemon |
| GET | /api/v2/app/preferences |
Get all preferences |
| POST | /api/v2/app/setPreferences |
Set preferences (JSON) |
| GET | /api/v2/torrents/info |
List torrents (with filter/sort/tag) |
| POST | /api/v2/torrents/add |
Add torrent (URL, file, or magnet) |
| POST | /api/v2/torrents/pause |
Pause torrent(s) |
| POST | /api/v2/torrents/resume |
Resume torrent(s) |
| POST | /api/v2/torrents/delete |
Delete torrent(s) (+ files) |
| POST | /api/v2/torrents/recheck |
Recheck torrent(s) |
| POST | /api/v2/torrents/reannounce |
Reannounce to trackers |
| POST | /api/v2/torrents/setCategory |
Set torrent category |
| POST | /api/v2/torrents/addTrackers |
Add trackers to torrent |
| POST | /api/v2/torrents/editTracker |
Replace tracker URL |
| POST | /api/v2/torrents/removeTrackers |
Remove trackers |
| GET | /api/v2/torrents/trackers |
List torrent trackers |
| GET | /api/v2/torrents/files |
List files in torrent |
| POST | /api/v2/torrents/filePrio |
Set file priority |
| POST | /api/v2/torrents/setShareLimits |
Set ratio/time share limits |
| POST | /api/v2/torrents/setLocation |
Change save path |
| POST | /api/v2/torrents/rename |
Rename torrent |
| POST | /api/v2/torrents/renameFile |
Rename file within torrent |
| POST | /api/v2/torrents/addTags |
Add tags |
| POST | /api/v2/torrents/removeTags |
Remove tags |
| GET | /api/v2/torrents/categories |
List all categories |
| POST | /api/v2/torrents/createCategory |
Create category |
| POST | /api/v2/torrents/removeCategories |
Remove categories |
| POST | /api/v2/torrents/setDownloadLimit |
Per-torrent DL limit |
| POST | /api/v2/torrents/setUploadLimit |
Per-torrent UL limit |
| POST | /api/v2/torrents/setForceStart |
Force start toggle |
| POST | /api/v2/torrents/setSuperSeeding |
Super seeding toggle |
| POST | /api/v2/torrents/toggleSequentialDownload |
Sequential download |
| POST | /api/v2/torrents/setFirstLastPiecePrio |
First/last piece priority |
| GET | /api/v2/transfer/info |
Global transfer stats |
| POST | /api/v2/transfer/setDownloadLimit |
Global DL limit |
| POST | /api/v2/transfer/setUploadLimit |
Global UL limit |
| POST | /api/v2/transfer/toggleSpeedLimitsMode |
Toggle alt speed |
| GET | /api/v2/app/cookies |
List cookies (v2.11.3+) |
| POST | /api/v2/app/setCookies |
Set cookies (v2.11.3+) |
| POST | /api/v2/torrents/addWebSeeds |
Add web seeds to torrent |
| POST | /api/v2/torrents/editWebSeed |
Edit web seed URL |
| DELETE | /api/v2/torrents/removeWebSeeds |
Remove web seeds |
| GET | /api/v2/sync/maindata |
Sync main data (polling) |
| GET | /api/v2/log/main |
Get event log |
6. Torrent State Values
| State | Meaning |
|---|---|
downloading |
Actively downloading |
uploading |
Seeding (download complete) |
pausedDL |
Paused while downloading |
pausedUP |
Paused while seeding |
queuedDL |
Queued for download |
queuedUP |
Queued for seeding |
checkingDL |
Checking/resuming download |
checkingUP |
Checking/resuming seed |
stalledDL |
No peers, waiting to download |
stalledUP |
No leechers, idle seeding |
metaDL |
Fetching magnet metadata |
missingFiles |
Files not found |
error |
Error state |
moving |
Moving files to new location |
7. Complete PowerShell Automation Example
# qbt.ps1 — one-liner status check
$cred = @{ host = 'localhost:8080'; username = 'admin'; password = 'adminadmin' }
uv run --with qbittorrent-api python -c @"
import qbittorrentapi, json
qbt = qbittorrentapi.Client(**$(@{ $cred | ConvertTo-Json -Compress }))
for t in qbt.torrents_info():
print(f'{t.hash[-8:]} | {t.name[:40]:40s} | {t.state:15s} | {t.progress*100:5.1f}%')
"@
aria2c — The Lightweight Torrent Sniper
Philosophy
One binary, zero config. aria2c torrent.torrent — done. Want HTTP too? Same command.
No daemon needed for one-shots. Want management? Enable RPC mode.
1. Basic Torrent Downloads
# Download from a .torrent file
aria2c ubuntu-24.04-desktop.torrent
# Download from magnet link
aria2c "magnet:?xt=urn:btih:..."
# Download from trackerless magnet (DHT)
aria2c --enable-dht --dht-listen-port=6881 "magnet:?xt=urn:btih:..."
# Download a .torrent from a URL (auto-follows if .torrent)
aria2c "https://example.com/file.torrent"
# Specify download directory
aria2c -d "D:/Downloads" "magnet:?xt=urn:btih:..."
# Continue an interrupted download
aria2c -c "magnet:?xt=urn:btih:..."
2. HTTP/FTP Downloads (Bonus)
# Simple HTTP download
aria2c "https://example.com/large-file.iso"
# Download from multiple mirrors (fast!)
aria2c -x 16 -s 16 "https://mirror1/file.iso" "https://mirror2/file.iso"
# Download with 16 connections per server
aria2c -x 16 -s 16 "https://example.com/big-file.zip"
# With speed limit
aria2c --max-overall-download-limit=5M "https://example.com/file.iso"
# Multiple files in parallel
aria2c -j 3 "https://example.com/file1.iso" "https://example.com/file2.iso"
# From file list (URIs separated by newline)
aria2c -i uris.txt
3. BitTorrent Seeding Control
# Seed until ratio 2.0, then stop
aria2c --seed-ratio=2.0 "file.torrent"
# Seed for 60 minutes, then stop
aria2c --seed-time=60 "file.torrent"
# Both conditions (whichever hits first)
aria2c --seed-ratio=2.0 --seed-time=120 "file.torrent"
# No seeding at all
aria2c --seed-time=0 "file.torrent"
# Upload speed limit per torrent
aria2c -u 500K "file.torrent"
# Overall upload limit
aria2c --max-overall-upload-limit=2M "file.torrent"
# Only download, don't seed (immediate stop after completion)
aria2c --seed-time=0 "file.torrent"
4. Select Specific Files
# First, see what's in the torrent
aria2c -S "file.torrent"
# Output shows file indices — download only specific files
aria2c --select-file=1,3,5-7 "file.torrent"
5. Tracker Management
# Add extra trackers
aria2c --bt-tracker="udp://tracker.opentrackr.org:1337,udp://tracker.coppersurfer.tk:6969" "file.torrent"
# Exclude specific trackers
aria2c --bt-exclude-tracker="http://dead-tracker.com/announce" "file.torrent"
# Remove ALL trackers and use DHT only
aria2c --bt-exclude-tracker="*" --enable-dht "file.torrent"
# Connect to DHT network
aria2c --enable-dht --dht-listen-port=6881 "file.torrent"
6. RPC Mode (Persistent Daemon)
For continuous management, run aria2 with RPC enabled:
# Start as RPC daemon (listens on localhost:6800)
aria2c --enable-rpc --rpc-listen-all=true --rpc-listen-port=6800 `
--rpc-secret=mysecret `
--continue=true `
--max-concurrent-downloads=5 `
--max-connection-per-server=16 `
--split=16 `
--min-split-size=10M `
--seed-ratio=2.0 `
--enable-dht `
--dht-listen-port=6881 `
--listen-port=6881-6999 `
--dir="D:/Downloads"
# Or with a config file
aria2c --conf-path="C:/Users/rx/.aria2/aria2.conf"
Sample aria2.conf:
enable-rpc=true
rpc-listen-all=true
rpc-listen-port=6800
rpc-secret=mysecret
continue=true
max-concurrent-downloads=5
max-connection-per-server=16
split=16
min-split-size=10M
seed-ratio=2.0
enable-dht=true
dht-listen-port=6881
listen-port=6881-6999
dir=D:/Downloads
max-overall-download-limit=0
max-overall-upload-limit=5M
7. RPC via curl (JSON-RPC)
# Add a magnet link via RPC
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.addUri",
"params": ["token:mysecret", ["magnet:?xt=urn:btih:..."]]
}'
# Tell status of a download
curl -s http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.tellStatus",
"params": ["token:mysecret", "gid"]
}'
# List active downloads
curl -s http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.tellActive",
"params": ["token:mysecret"]
}'
# Pause all
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.pauseAll",
"params": ["token:mysecret"]
}'
# Force pause all
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.forcePauseAll",
"params": ["token:mysecret"]
}'
# Unpause all
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.unpauseAll",
"params": ["token:mysecret"]
}'
# Get global stats
curl -s http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.getGlobalStat",
"params": ["token:mysecret"]
}'
# Remove download
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.remove",
"params": ["token:mysecret", "gid"]
}'
# Change global option (speed limit)
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.changeGlobalOption",
"params": ["token:mysecret", {"max-overall-download-limit": "5M"}]
}'
# Change per-download option
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.changeOption",
"params": ["token:mysecret", "gid", {"max-upload-limit": "100K"}]
}'
# Save session (writes downloads to session file)
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.saveSession",
"params": ["token:mysecret"]
}'
# Shutdown
curl -X POST http://localhost:6800/jsonrpc \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "aria2.shutdown",
"params": ["token:mysecret"]
}'
8. aria2c RPC Methods Reference
| Method | Description |
|---|---|
aria2.addUri |
Add download by URI(s) |
aria2.addTorrent |
Add download by torrent file |
aria2.addMetalink |
Add download by metalink file |
aria2.remove |
Remove download (graceful) |
aria2.forceRemove |
Remove download (immediate) |
aria2.pause |
Pause download |
aria2.pauseAll |
Pause all downloads |
aria2.forcePause |
Pause immediately |
aria2.forcePauseAll |
Pause all immediately |
aria2.unpause |
Resume download |
aria2.unpauseAll |
Resume all downloads |
aria2.tellStatus |
Get download status |
aria2.tellActive |
List active downloads |
aria2.tellWaiting |
List queued downloads |
aria2.tellStopped |
List completed/removed downloads |
aria2.getFiles |
List files in download |
aria2.getPeers |
List connected peers |
aria2.getServers |
List connected servers |
aria2.getUris |
List URIs for download |
aria2.changePosition |
Change queue position |
aria2.changeUri |
Add/remove URIs from download |
aria2.getOption |
Get per-download options |
aria2.changeOption |
Change per-download options |
aria2.getGlobalOption |
Get global options |
aria2.changeGlobalOption |
Change global options |
aria2.getGlobalStat |
Get global statistics |
aria2.purgeDownloadResult |
Clear completed results |
aria2.removeDownloadResult |
Remove specific result |
aria2.getVersion |
Get version info |
aria2.getSessionInfo |
Get session ID |
aria2.shutdown |
Graceful shutdown |
aria2.forceShutdown |
Immediate shutdown |
aria2.saveSession |
Save session to file |
Decision Guide: Which Tool to Use?
| You want to... | Use |
|---|---|
| Download one torrent and forget it | aria2c |
| Manage 50+ torrents with categories/tags | qBittorrent |
| Automate RSS feeds / series tracking | qBittorrent |
| Download via HTTP/FTP with multi-connection | aria2c |
| Integrated search across trackers | qBittorrent |
| Minimal setup, no daemon | aria2c |
| Full REST API for custom tooling | qBittorrent |
| Add a list of 20 magnets from a file | aria2c (-i magnets.txt) |
| Set per-torrent ratio limits | qBittorrent (better UX) |
| Low memory footprint | aria2c (4-9 MiB RAM) |
| Need categories, tags, file management | qBittorrent |
| One binary, works everywhere | aria2c |
| Need Web UI out of the box | qBittorrent |
Quick Reference: Common Tasks
| Task | qBittorrent (Python) | aria2c (CLI) |
|---|---|---|
| Add magnet | qbt.torrents_add(urls="magnet:...") |
aria2c "magnet:..." |
| Add .torrent file | qbt.torrents_add(torrent_files=["f.torrent"]) |
aria2c file.torrent |
| List all torrents | qbt.torrents_info() |
curl ... tellActive (RPC) |
| Pause all | qbt.torrents_pause("all") |
Ctrl+C then aria2c -c |
| Resume all | qbt.torrents_resume("all") |
aria2c -c |
| Delete with files | qbt.torrents_delete(delete_files=True, ...) |
N/A (stop + manual) |
| Set speed limit | qbt.torrents_set_download_limit(...) |
--max-overall-download-limit=5M |
| Add tracker | qbt.torrents_add_trackers(...) |
--bt-tracker="url" |
| Set category | qbt.torrents_set_category(...) |
N/A |
| Get progress | t.progress (0.0-1.0) |
Console readout / RPC |
| Run in background | bg run "qbittorrent-nox..." |
bg run "aria2c --enable-rpc..." |
Notes & Tips
- Firewall: Both tools need port access. aria2c uses
6881-6999for DHT/peers by default. qBittorrent also uses a listen port for incoming connections. Open these in your firewall. - DHT is your friend: When trackers are unreliable, DHT keeps swarms alive.
- qBittorrent: enabled by default in preferences
- aria2c:
--enable-dht --dht-listen-port=6881
- Session persistence:
- qBittorrent saves state automatically (resume data)
- aria2c: use
--save-session=/path/session.txtand--input-file=/path/session.txton restart
- Security: If you expose the Web UI or RPC to the internet, use a strong password and consider HTTPS + reverse proxy (nginx/caddy). Never use defaults on a public port.
- Logging:
- qBittorrent:
qbt.log.main()(Python) or/api/v2/log/main - aria2c:
--log=/path/aria2.log
- qBittorrent:
- The
Refererheader in qBittorrent is mandatory — every API call needs it. Missing it silently drops requests. This catches everyone at least once. - qBittorrent v5.x API changes are minimal but notable: cookie management
APIs were added (
app/cookies,app/setCookies), thecookiefield in/torrents/addwas removed, andreannouncewas added to torrent info. The Python clientqbittorrent-apiv2026.5.1 fully supports v5.2.0. See the v5 API docs for the full reference. - API Key Authentication (≥v5.2.0) is a new alternative to cookie-based auth —
generate an API key in the WebUI and use it directly:
See the wiki page for setup instructions.curl --header 'X-API-Key: your_key_here' http://localhost:8080/api/v2/torrents/info