Handling Files and Media in Telegram Bots
Receive, send, store and process files in Telegram bots — file_id reuse, size limits, downloading safely, media groups, and keeping disk and memory under control.
On this page
Photos, voice notes, documents, videos, stickers — media is a big part of Telegram, and many bots exist mainly to process it: converters, downloaders, archivers, moderation bots. Media handling has its own rules and limits, and doing it carelessly fills your disk or exhausts your memory. This article covers the essentials.
file_id: Telegram’s superpower
Every file Telegram stores has a file_id. When a user sends your bot a photo, the update includes its file_id, and you can send that same file anywhere by passing the ID — no download, no re-upload:
@router.message(F.photo)
async def echo_photo(message: Message):
largest = message.photo[-1] # photos come in several sizes
await message.answer_photo(largest.file_id, caption="Here it is again!")
This makes resending instant and free. A few rules:
- A
file_idis specific to your bot. Another bot can’t use it. - Save
file_ids you’ll reuse. If your bot sends the same welcome image to everyone, upload it once, store the returnedfile_id, and send by ID from then on. file_unique_idis for deduplication. It’s stable across bots and time, so it’s the right key for “have I seen this file before?” — but it can’t be used to download or send a file.
Size limits
On Telegram’s public Bot API server:
| Direction | Limit |
|---|---|
Bot downloads a file (getFile) |
20 MB |
| Bot uploads a file | 50 MB |
| Photo upload | 10 MB |
Users can send files up to 2 GB (more with Premium), so your bot will receive files it can’t download through the standard API. Handle this gracefully: check file_size before downloading and tell the user politely if it’s too large.
If you genuinely need bigger files, you can run your own local Bot API server — Telegram’s open-source telegram-bot-api — which lifts the download limit and allows uploads up to 2,000 MB. It’s a separate service you host, so it suits a VPS rather than a small bot plan.
Downloading files
Getting a file is a two-step process: call getFile to obtain a file_path, then download it. Libraries wrap this:
# aiogram 3
file = await bot.get_file(message.document.file_id)
await bot.download_file(file.file_path, destination=f"downloads/{message.document.file_unique_id}")
// grammY (with the files plugin: @grammyjs/files)
const file = await ctx.getFile();
const path = await file.download(`downloads/${file.file_unique_id}`);
Warning: The raw download URL has the form
https://api.telegram.org/file/bot<token>/<file_path>— it contains your bot token. Never log it, send it to users or put it in a web page.
Stream to disk, not memory
Downloading into memory is fine for small images. For documents and videos, write straight to disk so a 20 MB file doesn’t become a 20 MB spike in RAM — or several at once when users send files concurrently. Most library download helpers accept a destination path; use it.
Sending files
You can send files three ways:
- By
file_id— instant, for files already on Telegram. - By URL — Telegram fetches it from the web (with its own size limits, lower than direct uploads).
- By upload — from disk or memory.
from aiogram.types import FSInputFile, BufferedInputFile
await message.answer_document(FSInputFile("reports/weekly.pdf"))
await message.answer_photo(BufferedInputFile(png_bytes, filename="chart.png"))
Uploads return a message containing the new file_id — store it if you’ll send the file again.
Albums
sendMediaGroup sends up to ten photos or videos as one album. It counts as one action rather than ten, which is kinder to Telegram’s rate limits and to your users’ chat history.
Processing media
Converting audio, compressing video, generating thumbnails and running OCR are CPU-intensive. A few practices keep your bot responsive:
- Don’t block the event loop. Run tools like FFmpeg as subprocesses (
asyncio.create_subprocess_execin Python,child_process.spawnin Node.js), not synchronous calls. - Limit concurrency. Process a few files at a time with a semaphore or queue; ten simultaneous video conversions will saturate any modest CPU allocation.
- Tell the user it’s working. Send a chat action (
upload_document,record_video) or a “Processing…” message and edit it when done. - Time out. A malformed file can make a converter hang. Kill jobs that run too long.
sem = asyncio.Semaphore(2)
async def convert(src, dst):
async with sem:
proc = await asyncio.create_subprocess_exec(
"ffmpeg", "-y", "-i", src, "-vn", "-b:a", "128k", dst,
stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL,
)
await asyncio.wait_for(proc.wait(), timeout=120)
Keeping disk usage under control
Media bots fill disks faster than anything else. Plan cleanup from day one:
- Delete temporary files as soon as a job finishes, in a
finallyblock so failures clean up too. - Sweep old files with a scheduled job that removes anything older than a day in your downloads folder.
- Don’t store what Telegram already stores. If you only need to resend a file, keep the
file_id, not the file. - Watch the disk graph. A full disk makes databases fail and bots crash in confusing ways.
Kerit Cloud’s Telegram plans include 2 GB of NVMe storage on Free, 5 GB on Starter, 10 GB on Pro and 20 GB on Ultra — plenty for a well-behaved media bot, and disk is fast, which helps conversions.
Safety and responsibility
Bots that accept files from strangers should be careful:
- Never execute uploaded files, and be cautious with archives (zip bombs) and documents with macros.
- Check MIME types and extensions before processing, but don’t trust them blindly.
- Respect copyright. Downloader bots that redistribute copyrighted media can get your bot reported and your hosting suspended. Kerit Cloud’s terms prohibit hosting pirated content.
- Handle personal data with care. Photos and documents can contain sensitive information; delete them when you no longer need them.
Summary
Reuse files by file_id wherever possible, and use file_unique_id for deduplication. Respect the 20 MB download and 50 MB upload limits — or run a local Bot API server if you truly need more. Stream downloads to disk, keep your token out of download URLs, process media in limited-concurrency subprocesses, clean up temporary files, and keep an eye on disk usage.