---
title: "Telegram Bot Media API — Audio by file_id | FastSaverAPI"
url: https://fastsaverapi.com/telegram-bot-media-api/
description: "POST a YouTube video ID and get a Telegram audio file_id scoped to your bot: 7 credits when the track is cached, 15 on a miss, and no 50 MB upload ceiling."
updated: 2026-08-10
api_base: https://api.fastsaver.io/v1
site_index: https://fastsaverapi.com/llms.txt
openapi: https://fastsaverapi.com/openapi.json
---

# Telegram bot media API — audio without the re-upload

The Telegram bot media API turns a YouTube video ID into a Telegram file_id , so
your bot answers with a string instead of a file. Millions of tracks are already cached and cost 7
credits; a track we have to fetch first is slower and costs 15. Charts, search and song
recognition from the same API all hand you an ID this call accepts.

- **Endpoint**: POST /youtube/audio/tg-bot

- **Credits**: 7 on a cache hit, 15 on a miss

- **Returns**: a Telegram file_id

- **Scope**: valid only for the bot_username you send

## what a file_id is, precisely

When a bot sends a file, Telegram stores it and returns an identifier: a
file_id , an opaque string like CQACAgIAAxkDAAIVKWk… . Not a URL, not a
hash of the audio — a pointer into Telegram's storage, and the cheapest thing a bot can send,
because sending it moves no bytes through your infrastructure.

Two properties shape the design. A file_id is bound to one bot; Telegram will not
accept one bot's identifier from another. The same file also carries a
file_unique_id that is stable across bots, but you cannot send or download
with it — it only tells you two references point at the same audio.

The trade: pay once for a bot-scoped reference, and every later send of that track is one
ordinary Telegram call carrying a string. Store the ones you get — the first resolve is the only
part that costs credits, and it costs more than you might expect when the track is new to us.

## the loop this replaces

Otherwise, delivering one song means: resolve the track, stream it from a CDN to your server,
hold it in memory or on disk, upload the same bytes to Telegram as multipart form data, then keep
the file_id so the next user is cheaper.

- Bandwidth is doubled. Every megabyte arrives at your server and leaves again. On metered egress that is the cost of the feature.
- The upload leg is the slow one. Pulling from a CDN is fast; pushing to Telegram from a small VPS usually is not.
- 50 MB is a hard ceiling. The Bot API refuses larger uploads, so long mixes cannot be delivered this way at all.
- Concurrency becomes capacity planning. Twenty requests at once means twenty in-flight files, temp-file cleanup and a worker queue.

The tg-bot endpoint removes all four: the file never touches your process.

## the request

One POST, two fields. video_id is the YouTube or YouTube Music ID — the eleven
characters, not the whole link — and bot_username is your bot, with the @ .

POST
https://api.fastsaver.io/v1/youtube/audio/tg-bot
7 cached · 15 on a miss



```
curl -X POST https://api.fastsaver.io/v1/youtube/audio/tg-bot \
  -H "X-Api-Key: fs_sk_•••••••••••" \
  -H "Content-Type: application/json" \
  -d '{"video_id": "vk6014HuxcE", "bot_username": "@your_bot"}'
```



```
{
  "ok": true,
  "file_id": "CQACAgIAAxkDAAIVKWk..."
}
```


That is the entire response: a delivery handle, no title, no artist, no duration. Keep the
metadata from whatever call gave you the video_id — you will want it for the
caption.

The same response shape covers both prices. If we already hold the track the call is 7 credits
and returns almost immediately; if we do not, the track is downloaded first and the call is
billed at the YouTube download rate of 15. Nothing in the payload tells you which happened, so
read your spend from GET /balance or your dashboard rather than assuming.

Treat ok as the success flag rather than the status code. A refusal arrives as a
plain 400 with a detail string — an exhausted balance reads
Insufficient credits. Please top up to your account. , and there is no
402 to branch on. 401 means the key never arrived, 429
that you are over your plan's per-minute limit. A failed resolve is not free either: every
failure path charges a flat 0.1 credits, so a retry loop over an ID that will never
resolve still costs something.

## the smallest bot that sends one

The endpoint reduces to two lines of bot code: POST the video ID, pass the returned string to
answer_audio . Everything below is scaffolding around those two lines.



```
import asyncio, os
import httpx
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message

API = "https://api.fastsaver.io/v1"
BOT_USERNAME = "@your_bot"

bot = Bot(os.environ["BOT_TOKEN"])
dp = Dispatcher()
http = httpx.AsyncClient(
    headers={"X-Api-Key": os.environ["FASTSAVER_KEY"]}, timeout=120,
)


@dp.message(F.text.regexp(r"^[\w-]{11}$"))
async def on_video_id(msg: Message) -> None:
    r = await http.post(
        f"{API}/youtube/audio/tg-bot",
        json={"video_id": msg.text, "bot_username": BOT_USERNAME},
    )
    data = r.json()
    if not data.get("ok"):
        await msg.answer(data.get("detail", "could not resolve that ID"))
        return
    await msg.answer_audio(data["file_id"])          # a string, not a file


if __name__ == "__main__":
    asyncio.run(dp.start_polling(bot))
```

On python-telegram-bot the send becomes
await update.message.reply_audio(audio=file_id) . Either way, the argument that would
normally be an open file object is a string.

This is deliberately the endpoint and nothing else. Search, an inline keyboard, storing the
strings and the error paths belong to a real bot rather than to the call —
the Telegram music bot guide builds that end to end.

## what else fits a bot

A bot is rarely just a downloader. Most other endpoints hand you a video_id that
goes straight into the tg-bot call.

- GET /youtube/search — 2 credits. Ten results per page, pages 1 to 3, each with video_id , title , duration and thumbnails: enough to label a row without a second lookup.
- GET /shazam/top — 1 credit. Charts by country code or worldwide; results already carry video_id .
- POST /shazam/identify — 5 credits, mp3/m4a/ogg/mp4 up to 50 MB. Bots may download from Telegram up to 20 MB, so a forwarded voice note fits: pull it with getFile , post it, offer the results back.
- GET /shazam/lyrics — 2 credits, by title and artist . Both are required query parameters — there is no lookup by ID — and the reply carries ok and lyrics and nothing else, so keep the title and artist you already have for the header of your "lyrics" button.
- POST /youtube/download with format: "audio" — 15 credits, when you need the file rather than a reference.
- GET /balance — free. Poll it from a health check; learn about an empty balance before your users do.

GET /fetch covers the other platforms with a direct download_url .
Telegram will fetch a remote URL for you, but its ceiling there is lower than the upload one — a
convenience, not a strategy.

## what a file_id will not do for you

- One bot, one file_id. Swap or add a bot and every reference must be re-requested, and each re-request is charged again. Keep bot_username alongside the string.
- Public content only. Public YouTube and YouTube Music IDs resolve. Private, members-only and region-blocked videos do not.
- A miss is slower and costs 15 instead of 7. A cache hit hands back a track we already hold; a miss means downloading it first, and that is billed at the YouTube download rate. Storing the file_id yourself therefore saves real money and not just latency: every re-resolve you avoid is 7 credits you keep, and the first resolve of a track nobody has asked for yet is 15.
- Two rate limits apply. Ours is per plan — 10 requests a minute on Free, 60 on Pro, up to 900 on Mega. Telegram runs its own flood control on top of that.
- References are not permanent. A file_id points into storage you do not own. On a failed send, request a fresh one.
- Platforms move. When YouTube changes something the fix ships on our side; your request shape stays put.

## questions about Telegram bot delivery

**Q: What exactly is a Telegram file_id?**

A: An opaque string Telegram issues for a file already sitting on its servers. A handle, not
a URL: nothing to open, nothing to download. You put it where the file would go in
sendAudio and Telegram serves the bytes.

**Q: Can I reuse the same file_id in a second bot?**

A: No. It is scoped to one bot; hand it to another and Telegram rejects the send with a
bad-request error about the file identifier. Hence bot_username being required.
Two bots means requesting the track twice and keeping two strings.

**Q: What does sending a track through a Telegram bot cost?**

A: 7 credits per call to POST /youtube/audio/tg-bot when we already hold the
track, 15 when we do not and have to download it first — a miss is billed as a YouTube
download. Add 2 for GET /youtube/search if that is how you found the ID. Hold on
to the returned string and later sends are ordinary Telegram calls that spend nothing.

**Q: What happens when the track is not already cached?**

A: It is downloaded fresh and you still get a file_id — but the call costs 15
credits instead of 7, because there is nothing to reference until the download finishes. It is
slower for the same reason. Millions of tracks are pre-cached, so misses are the exception —
but budget for them, and give the HTTP call a read timeout measured in minutes, not the
handful of seconds most clients default to.

**Q: Can a Telegram bot send audio larger than 50 MB?**

A: Not by uploading it — the Bot API caps a bot upload at 50 MB, which a long mix or podcast
episode exceeds. A file_id sidesteps that: your bot uploads nothing, it references
a file Telegram already holds. Self-hosting a Bot API server raises the ceiling, but that
server is then yours to keep alive.

## Keep reading

- Build a Telegram music bot — search, keyboard, cache, errors
- YouTube downloader API — info, search, download
- Shazam API — identify a voice note
- Endpoint reference & playground — run the call in the browser

## Send your first file_id

Free tier is 1,000 credits — around 140 cached tracks, or 66 if every one is new to the cache.

Get an API key
Open the playground
