saveapi.org

YouTube to MP3 with an API — two calls, one signed link

There is no “give me the MP3” endpoint that hides the work, because the file does not exist until it is built. Instead there are two honest calls: info tells you what the audio weighs, create builds it and returns a link with an expiry. This guide walks through both and what to do with the link.

Updated

Ask what the audio weighs

info answers with audio_formats[]: an m4a row with the exact byte size and an mp3 row whose size is an estimate. Title, duration and thumbnail come along, so a bot can show the user what it is about to send before spending 10 credits on the file.

Terminal
curl -s "https://api.saveapi.org/v1/youtube/info" \
  -H "Authorization: Bearer sk_live_xxxx" \
  -G --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ"

Build the file

The same link plus quality=mp3 (or m4a). The response carries url, filename with the title already in it, mode "audio", file_size and expires. Nothing is stored on our side: the bytes are produced as you read the link.

Terminal
curl -s "https://api.saveapi.org/v1/youtube/create" \
  -H "Authorization: Bearer sk_live_xxxx" \
  -G --data-urlencode "url=https://www.youtube.com/watch?v=aqz-KE-bpKQ" \
  --data-urlencode "quality=mp3"

Read the link correctly

It is a stream, not a stored file: no Content-Length, no Range, one pass from start to end. After the expires timestamp it answers 410 — call create again rather than keeping links around. Treat it as a download that has to happen now.

Fetch it and hand it on

Stream the response to disk under the filename you were given, then send the file from your bot or store it where you need it. Send the file, not the link: Telegram fetches URLs itself with its own limits (20 MB for audio by URL), and a stream without a declared size is a poor fit for that.

Python
import httpx

def fetch_audio(made: dict, dest: str) -> str:
    """Pull the file behind a /v1/youtube/create link onto disk."""
    path = f"{dest}/{made['filename']}"
    with httpx.stream("GET", made["url"], timeout=None) as r, open(path, "wb") as f:
        if r.status_code == 410:            # past expires: call create again
            raise RuntimeError("link expired")
        for chunk in r.iter_bytes():        # no Content-Length: read to the end
            f.write(chunk)
    return path

# made = youtube_link(url, "mp3")          # from the yt-dlp guide
# audio = fetch_audio(made, "/tmp")
# bot.send_audio(chat_id, open(audio, "rb"), title=made["title"])

MP3 or M4A

M4A is the AAC track YouTube publishes: exact size, no transcoding, the stream starts immediately. MP3 is built from it with ffmpeg, so it takes longer to begin and its size is estimated. Pick M4A unless the player on the other end insists on MP3 — Telegram plays both.

Questions before you build

Do I have to call info first?
No. create works on its own with quality=mp3 or m4a — 10 credits. info is for showing the size or the title before you commit, and it costs 2.
Can I choose the bitrate?
No. One audio rendition per format: the best M4A the source publishes and a single MP3 encode. The quality parameter accepts mp3 or m4a.
What if the video is unavailable?
The response has success: false and a stable code from the same error table as every other platform. A link YouTube itself refuses costs 0.1 credit; a failure on our side costs nothing and is safe to retry.

Platform references used here

Other guides

Run this guide with your own key

Key in under a minute. 1,000 free credits, no card, no expiry.

Get a key and try itFull API reference