tg_backup
Incremental media backup for Telegram chats. Downloads photos and documents from a chat, remembers where it stopped, and picks up from there on the next run.
Single Python file, one dependency (Telethon).
Install
pip install telethon
cp tg_backup.py ~/bin/ && chmod +x ~/bin/tg_backup.py
Setup
Run it once with no arguments to create the config:
tg_backup.py
It writes ~/.config/telegram_backup/config.json and exits. Fill in the two values from my.telegram.org (API development tools):
{
"api_id": 12345,
"api_hash": "<your 32-character hash>"
}
api_id is an integer, api_hash a 32-character hex string.
Nothing else from that page is needed. FCM credentials and MTProto server addresses are for full client implementations.
The first real run prompts for your phone number and login code. That session is saved and shared by every subsequent run, so you log in once.
Usage
List your chats to find a target:
tg_backup.py --list-chats
ID TYPE USERNAME NAME
-1001234567890 group @examplegroup Example Group
123456789 user @examplecontact Example Contact
987654321 user - Contact Without Handle
Back one up:
tg_backup.py --target=@examplegroup --archive-dir ~/backups/examplegroup
tg_backup.py --target=-1001234567890 --archive-dir ~/backups/examplegroup
Numeric IDs are more durable than usernames, which their owners can change. Chats showing - have no public handle and can only be addressed by ID.
--target also accepts me (Saved Messages), t.me links, and invite links for groups you have already joined.
Options
| Option | Default | Meaning |
|---|---|---|
--target |
required | Chat to back up: @username, numeric ID, t.me link, or me |
--archive-dir |
./tg_archive |
Where media and resume state are written; created if missing |
--list-chats |
Print your dialogs with IDs and usernames, then exit | |
--self-check |
Run internal assertions and exit. No network, no login |
Use one archive directory per chat. Prefer absolute paths: the default is relative to the current directory, so running from elsewhere silently starts a second archive.
Negative IDs need =
tg_backup.py --target=-1001234567890 # correct
tg_backup.py --target -1001234567890 # error: expected one argument
Without =, argparse reads the leading - as the start of another flag.
How it works
Messages are processed oldest to newest, and the last message ID seen is written to state.json in the archive directory after every message. An interrupted run resumes from there instead of rescanning the whole chat.
Media is saved as {message_id}_{original_name}. Downloads land in a temporary .part file and are renamed only on success, so a killed run never leaves a truncated file that a later run would mistake for a complete one. Files that already exist are skipped, so even a lost state.json degrades to a slow rescan rather than re-downloading everything.
Rate limits are handled at two levels: Telegram's requested wait is honoured for both individual downloads and history fetches, and other errors (dropped connections, expired file references) are retried with exponential backoff. A download that fails permanently is logged and skipped rather than stalling the run forever.
Only media is saved. Message text and captions are not.
Files
| Path | Contents |
|---|---|
~/.config/telegram_backup/config.json |
API credentials |
~/.config/telegram_backup/session.session |
Login session, shared across all archive directories |
<archive-dir>/state.json |
Resume position for that chat |
<archive-dir>/* |
Downloaded media |
The session file is account credentials: anyone holding it can read your Telegram. It is created mode 0600 inside a 0700 directory. Keep it out of version control and off shared storage.
License
Copyright (C) 2026 Danilo M. danix@danix.xyz
Released under the GNU General Public License version 2. See LICENSE for the full text.
Development Approach
This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback.
All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions.
The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.
