telegram-sticker-downloader
Download Telegram sticker packs to your disk — one folder per pack, in the file formats you ask for.
- Downloads static (
.webp), animated (.tgs), video (.webm) stickers and PNG thumbnails, filtered to whichever types you want. - Downloads files in parallel, with retries and Telegram rate-limit handling.
- Resumable: files already on disk are skipped without spending API calls.
- Writes a
pack.jsonper pack with each sticker's emoji, size and file names. - Works as a scriptable CLI and as the original interactive prompt.
Requirements
- Python 3.10 or newer
- A Telegram bot token (free, takes a minute — see below)
Install
git clone https://git.hiddenden.cafe/Hiddenden/telegram-sticker-downloader
cd telegram-sticker-downloader
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e . # installs the `tg-stickers` command
# or, without installing the package:
pip install -r requirements.txt
Get a bot token
- Open Telegram and message @BotFather.
- Send
/newbotand follow the prompts. - Copy the token it gives you.
- Save it next to the project:
cp example.env .env
# then edit .env:
# TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
The token can also come from the TELEGRAM_BOT_TOKEN environment variable or
from --token. You do not need to add the bot to any chat — reading public
sticker packs is enough.
Usage
# One pack
tg-stickers https://t.me/addstickers/SomePack
# Several packs, only the static images and thumbnails
tg-stickers SomePack AnotherPack --types webp,png
# Everything listed in urls.txt, 16 files at a time
tg-stickers --from-file urls.txt --concurrency 16
# From a pipe
printf 'PackOne\nPackTwo\n' | tg-stickers --from-file -
# See what would happen without writing anything
tg-stickers --from-file urls.txt --dry-run
# No arguments: the original interactive prompts
tg-stickers
Without installing the package, the same thing works as
python -m sticker_downloader ... or python app.py ....
Pack references
Anything recognisable is accepted, so you can paste straight from Telegram:
https://t.me/addstickers/SomePack t.me/addstickers/SomePack
https://telegram.me/addstickers/Pack tg://addstickers?set=SomePack
https://t.me/addemoji/SomePack SomePack
In a --from-file list, blank lines and # comments are ignored, and duplicate
packs are downloaded only once.
Options
| Option | Description |
|---|---|
PACK ... |
One or more pack URLs / names |
-f, --from-file PATH |
Read references from a file, one per line (- for stdin) |
-t, --types LIST |
webp, tgs, webm, png, comma-separated, or all (default: all) |
-o, --output DIR |
Where packs are written (default: downloads) |
-c, --concurrency N |
Files downloaded in parallel per pack (default: 8) |
--retries N |
Attempts per network call (default: 3) |
--overwrite |
Re-download files that already exist |
--emoji-names |
Put each sticker's emoji in its file name |
--no-metadata |
Skip writing pack.json |
--no-pack-thumbnail |
Skip the pack's own cover image |
--dry-run |
Report what would be downloaded, write nothing |
--token TOKEN |
Bot token (overrides the environment) |
--env-file PATH |
dotenv file to read the token from (default: .env) |
-v, --verbose |
Log every file individually |
-q, --quiet |
Only report fatal errors |
--version |
Print the version |
File types
| Type | What it is | Notes |
|---|---|---|
webp |
Static stickers | Regular image, opens anywhere |
tgs |
Animated stickers | Gzipped Lottie JSON, not a video file |
webm |
Video stickers | VP9 with alpha |
png |
Thumbnails | Small preview Telegram generates per sticker |
A sticker has exactly one primary type — --types tgs on a pack of static
stickers downloads nothing but the thumbnails you asked for.
Output
downloads/
└── SomePack/
├── 001.webp
├── 001_thumb.png
├── 002.webp
├── 002_thumb.png
├── _pack_thumbnail.webp
└── pack.json
Files are numbered in pack order. With --emoji-names they become
001_🦊.webp. pack.json records what was downloaded:
{
"pack": {
"reference": "https://t.me/addstickers/SomePack",
"name": "SomePack",
"title": "Some Pack",
"sticker_type": "regular"
},
"generated_at": "2026-07-27T12:00:00+00:00",
"downloader_version": "1.0.0",
"requested_file_types": ["webp", "png"],
"sticker_count": 2,
"stickers": [
{
"index": 1,
"emoji": "🦊",
"file_unique_id": "AgADBAADwqbcCw",
"type": "regular",
"width": 512,
"height": 512,
"is_animated": false,
"is_video": false,
"files": ["001.webp", "001_thumb.png"]
}
]
}
How it works
Packs are processed one at a time so progress stays readable; the files inside a
pack download concurrently, bounded by --concurrency.
- Resume. Existing files are detected before any API call, so re-running a
large batch costs almost nothing. Use
--overwriteto force a refresh. - Atomic writes. Files land via a temporary
.partfile, so an interrupted run never leaves a half-written sticker that a later run would skip. - Retries. Timeouts, connection errors and HTTP 429/5xx are retried with
exponential backoff; Telegram's own
retry_afterhint is respected. Genuine errors (unknown pack, bad token) fail immediately instead of being retried. - Partial failure is not fatal. A file that cannot be downloaded is reported and the rest of the pack continues; the run's exit code reflects it.
Exit codes
| Code | Meaning |
|---|---|
0 |
Everything downloaded |
1 |
One or more packs or files failed |
2 |
Bad arguments, missing/rejected token, or aborted |
Development
pip install -r requirements-dev.txt
pytest # test suite
ruff check . # lint
ruff format . # format
The tests use fakes for the Telegram bot, plus a real local HTTP server for the download layer, so the whole suite runs offline in well under a second.
.gitea/workflows/ci.yml runs lint and tests on Python 3.10–3.13. It needs
Gitea Actions enabled on the repository and a registered act_runner; without a
runner the file is simply ignored.
Layout:
| Module | Responsibility |
|---|---|
sticker_downloader/cli.py |
Argument parsing, interactive prompts, wiring |
sticker_downloader/downloader.py |
Planning and downloading a pack |
sticker_downloader/urls.py |
Pack-reference parsing |
sticker_downloader/config.py |
Settings and file-type handling |
sticker_downloader/fetcher.py |
HTTP layer behind a small protocol |
sticker_downloader/retry.py |
Retry policy and backoff |
sticker_downloader/progress.py |
Console reporting |
sticker_downloader/results.py |
Result and totals objects |
Limitations
- The Bot API only serves files up to 20 MB. Stickers are far smaller, so this is not normally a concern.
.tgsand.webmare downloaded as-is; no conversion to GIF/APNG is done.- Private or deleted packs cannot be read, and are reported as
sticker pack not found.
License
MIT — see LICENSE.