Metadata-Version: 2.5
Name: fastsaver
Version: 0.1.0
Summary: Python SDK for FastSaverAPI — download media from Instagram, TikTok, YouTube (up to 4K + MP3), Pinterest, Facebook, X, RuTube, Likee; Telegram file_id audio; Shazam. Sync + async.
Project-URL: Homepage, https://api.fastsaver.io
Project-URL: Documentation, https://api.fastsaver.io/docs
Project-URL: Repository, https://github.com/coder2077/fastsaver
Project-URL: Issues, https://github.com/coder2077/fastsaver/issues
Project-URL: Changelog, https://github.com/coder2077/fastsaver/blob/main/CHANGELOG.md
Project-URL: Telegram, https://t.me/fastsaverapi
Author-email: FastSaverAPI <support@fastsaver.io>
License-Expression: MIT
License-File: LICENSE
Keywords: facebook video downloader,fastsaver,fastsaverapi,instagram downloader api,media downloader,pinterest downloader,shazam api,telegram bot audio,telegram file_id,tiktok downloader api,tiktok no watermark,twitter video downloader,youtube download api,youtube mp3 api
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1.0,>=0.24
Description-Content-Type: text/markdown

# fastsaver — Instagram, TikTok and YouTube downloader API for Python (FastSaverAPI SDK)

**One Python API to download Instagram reels and posts, TikTok videos without watermark, YouTube
as MP4 (up to 4K) or MP3, plus Pinterest, Facebook, X/Twitter, RuTube and Likee. Includes a Shazam
song-recognition API and YouTube audio delivered as a Telegram `file_id` for music bots. Sync and
async. Free tier: 1,000 credits, no credit card.**

[![PyPI](https://img.shields.io/pypi/v/fastsaver.svg)](https://pypi.org/project/fastsaver/)
[![Python versions](https://img.shields.io/pypi/pyversions/fastsaver.svg)](https://pypi.org/project/fastsaver/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/coder2077/fastsaver/blob/main/LICENSE)
[![CI](https://github.com/coder2077/fastsaver/actions/workflows/ci.yml/badge.svg)](https://github.com/coder2077/fastsaver/actions/workflows/ci.yml)
[![Checked with mypy](https://img.shields.io/badge/mypy-checked-blue.svg)](https://mypy-lang.org/)
[![Free 1,000 credits](https://img.shields.io/badge/free_tier-1%2C000_credits%2C_no_card-brightgreen.svg)](https://api.fastsaver.io/auth)
[![Telegram](https://img.shields.io/badge/Telegram-@fastsaverapi-26A5E4.svg?logo=telegram)](https://t.me/fastsaverapi)

```python
from fastsaver import FastSaver

client = FastSaver("fs_sk_...")  # or export FASTSAVER_API_KEY
video = client.fetch("https://www.tiktok.com/@scout2015/video/6718335390845095173")
print(video.download_url)        # direct MP4 link, no watermark
```

> **Start free: 1,000 credits, no credit card; paid plans from $9/month for 100,000 credits.**
> Create a key at **[api.fastsaver.io/auth](https://api.fastsaver.io/auth)** (email, Google or
> Telegram login), `pip install fastsaver`, and you are downloading in under a minute.

[Sign up](https://api.fastsaver.io/auth) · [API docs](https://api.fastsaver.io/docs) ·
[Pricing](https://api.fastsaver.io/pricing) · [Telegram community](https://t.me/fastsaverapi) ·
[support@fastsaver.io](mailto:support@fastsaver.io)

<!--
SOCIAL PROOF (fill only with real, verifiable figures; delete this block otherwise):
> Powering **N** Telegram bots and **N million** downloads a month.
> "Quote from a real customer" — Name, project (link)
-->

---

## Table of contents

- [Why FastSaverAPI](#why-fastsaverapi) · [vs yt-dlp / instaloader](#fastsaverapi-vs-yt-dlp-instaloader-and-diy-scrapers)
- [Platforms, credit costs and plans](#supported-platforms-credit-costs-and-plans)
- [Install](#install) · [Get your API key](#get-your-api-key) · [Quickstart](#quickstart-sync) · [Async and bulk](#async-client-and-bulk-downloads)
- [Instagram](#instagram-downloader-api-reels-posts-stories) · [TikTok (no watermark)](#tiktok-downloader-api-no-watermark) · [YouTube MP4 / MP3](#youtube-to-mp4-and-mp3-download-api-up-to-4k) · [Telegram music bot](#telegram-music-bot-youtube-audio-as-a-file_id) · [Pinterest, Facebook, X, RuTube, Likee](#pinterest-facebook-xtwitter-rutube-and-likee-video-downloader) · [Shazam](#shazam-api-for-python-identify-songs-top-charts-lyrics)
- [Balance](#balance-and-usage) · [Errors](#error-handling) · [Retries, timeouts, proxies](#retries-timeouts-proxies) · [CLI](#command-line-interface)
- [FAQ](#faq) · [Examples](#examples) · [Support](#support-contributing-license)

## Why FastSaverAPI

Scraping social networks yourself means proxies, cookies, IP bans, captchas and a parser that
breaks every time Instagram or TikTok ships an update. **FastSaverAPI is a hosted social media
downloader API**: you send a link, you get back direct download URLs and metadata in one stable
JSON shape. No browser automation, no yt-dlp upgrades at 3 a.m., no servers to babysit.

- **Free to start** — 1,000 credits on sign-up, no credit card, 10 requests/minute.
- **8 platforms, one endpoint** — Instagram, TikTok, Pinterest, Facebook, X/Twitter, RuTube,
  Likee via `client.fetch(url)`, plus a full YouTube toolset. Every result has the same fields.
- **TikTok without watermark**, Instagram reels/carousels/stories, YouTube up to **2160p (4K)**
  and **MP3/audio**.
- **Built for Telegram bots** — turn a YouTube video into a Telegram `file_id` in one call,
  backed by a cache of millions of tracks. Your bot never downloads or uploads a byte.
- **Shazam built in** — recognise a song from a voice message, browse charts, fetch lyrics.
- **Predictable billing** — a fixed credit price per call; a failed resolve costs 0.1 credit;
  bad key, no credits or rate-limit rejections cost nothing.
- **Fast** — Instagram and TikTok fetches typically answer in under a second; popular posts
  come straight from cache.

### Why this SDK

- Sync **and** async clients with identical method names: `FastSaver` / `AsyncFastSaver`.
- Typed, frozen dataclasses for every response — the full JSON is always on `.raw`.
- Precise exceptions: `AuthenticationError`, `InsufficientCreditsError`, `RateLimitError`,
  `NotFoundError`, `FetchError`, and `InvalidArgumentError` for mistakes caught before a request.
- Automatic retries with exponential backoff for network errors, 5xx and 429.
- Streaming `save()` that writes any download link to disk without leaking your API key.
- A `fastsaver` command-line tool for quick checks and shell scripts.
- Zero heavy dependencies — just `httpx`. Python 3.9+. Fully type-hinted (`py.typed`), checked
  with `mypy --strict`.
- 800+ mocked test cases (`respx`) run in CI on Python 3.9 through 3.14 — `uv run pytest`
  needs no API key.

## FastSaverAPI vs yt-dlp, instaloader and DIY scrapers

`yt-dlp`, `instaloader` and friends are great tools. They run on **your** machine, which is the
whole difference:

| | Self-hosted scrapers (yt-dlp, instaloader, custom) | FastSaverAPI + `fastsaver` |
|---|---|---|
| Keeps working when platforms change | You update, redeploy, and hope | Handled server-side, same JSON |
| Proxies, cookies, IP bans, captchas | Your problem, your cost | Included |
| TikTok without watermark | Depends on the current extractor | Yes |
| YouTube 4K + MP3 conversion | Needs ffmpeg and CPU on your server | Done upstream, you get a link |
| Telegram delivery | Download → upload to Telegram (≤ 50 MB) | `file_id` in one call, no upload |
| Song recognition (Shazam) | Separate service | Built in |
| Time to first working call | Hours to days | Minutes — 1,000 free credits |
| Marginal cost per link | $0 (plus servers, proxies, your time) | From ~$0.14 per 1,000 links on Pro |
| Works offline / no third party | Yes | No — it is a hosted API |
| Cost model | Servers, proxies, maintenance time | From $0; Pro at $9 / 100,000 credits |

**When yt-dlp is still the right tool:** one-off personal downloads, archiving your own channel
at zero marginal cost, or environments that must not call an external service. FastSaverAPI is
for apps and bots that need to keep working every day without you maintaining scrapers.

## Supported platforms, credit costs and plans

| Platform | What you can download | Credits / request |
|---|---|---|
| **Instagram** | Posts, reels, IGTV, carousels (albums) | 1.5 |
| **Instagram** | Stories and highlights | 5 |
| **TikTok** | Videos **without watermark**, photo posts, the original sound | 1.5 |
| **YouTube** | Video info and available formats | 2 |
| **YouTube** | Music search (YouTube Music) | 2 |
| **YouTube** | Download MP4 144p–1080p, or audio/MP3 | 15 |
| **YouTube** | Download 1440p (2K) / 2160p (4K) | 25 |
| **YouTube → Telegram** | Audio as a Telegram `file_id` | 7 (cached) · 15 (first time) |
| **Pinterest** | Pins, videos, idea pins (`pinterest.com`, `pin.it`) | 1.5 |
| **Facebook** | Videos and reels (`facebook.com`, `fb.com`, `fb.watch`) | 1.5 |
| **X / Twitter** | Videos, GIFs and images (`x.com`, `twitter.com`) | 1.5 |
| **RuTube** | Videos (`rutube.ru`, `rutube.com`) | 3 |
| **Likee** | Videos (`likee.video`, `likee.com`) | 3 |
| **Shazam** | Identify a song from audio | 5 |
| **Shazam** | Top charts by country | 1 |
| **Shazam** | Lyrics by title and artist | 2 |
| **Account** | Balance and plan | free |

Credits are consumed per successful call. Paid plans start at **$9/month**. Rate limits are per
API key in a fixed 60-second window, shared across `/fetch`, `/youtube/*` and `/shazam/*`.

| Plan | Price / month | Credits / month | Requests / min | What that buys |
|---|---|---|---|---|
| **Free** | $0 — no card | 1,000 | 10 | ~660 Instagram/TikTok fetches, or ~66 YouTube downloads, or ~140 cached Telegram tracks |
| **Pro** | $9 | 100,000 | 60 | ~66,000 fetches, or ~6,600 YouTube downloads, or ~14,000 cached Telegram tracks |
| **Ultra** | $39 | 500,000 | 250 | ~333,000 fetches, or ~33,000 YouTube downloads |
| **Mega** | $99 | 1,500,000 | 900 | ~1,000,000 fetches, or ~100,000 YouTube downloads |

On Pro that works out to roughly **$0.14 per 1,000 Instagram/TikTok links**, **$1.35 per 1,000
YouTube MP4/MP3 downloads** and **$0.63 per 1,000 cached Telegram audio tracks** (Ultra and Mega
are cheaper per credit). A failed resolve (private, deleted, unsupported URL) costs 0.1 credit;
rejected requests (bad key, no credits, rate limit) cost nothing. Pay by card or crypto. Current
prices and plan details: <https://api.fastsaver.io/pricing>.

## Install

```bash
pip install fastsaver
```

```bash
uv add fastsaver
```

Requires Python 3.9 or newer. The only dependency is [`httpx`](https://www.python-httpx.org/).

## Get your API key

1. Sign up at [api.fastsaver.io/auth](https://api.fastsaver.io/auth) — email code, Google or
   Telegram login. No card needed for the Free plan.
2. Copy your key from the [dashboard](https://api.fastsaver.io/dashboard). It looks like `fs_sk_...`.
3. Export it so `FastSaver()`, `AsyncFastSaver()` and the CLI find it automatically:

```bash
export FASTSAVER_API_KEY="fs_sk_..."          # macOS / Linux
```

```powershell
$env:FASTSAVER_API_KEY = "fs_sk_..."          # Windows PowerShell
```

Or pass it explicitly: `FastSaver(api_key="fs_sk_...")`. Without a key the client raises
`ConfigurationError` at construction time, before any request.

## Quickstart (sync)

```python
from fastsaver import FastSaver

with FastSaver() as client:  # reads FASTSAVER_API_KEY
    result = client.fetch("https://www.instagram.com/p/DRsmm9UjKfH/")

    print(result.source)      # instagram.com
    print(result.type)        # image | video | album
    print(result.caption)

    if result.is_album:
        # Carousels have no top-level download_url — iterate the items
        for item in result.items:
            print(item.type, item.download_url, item.width, item.height)
    else:
        print(result.download_url, result.width, result.height, result.duration)

    # Every download link, regardless of type
    for url in result.download_urls:
        print(url)

    print(result.raw)         # the JSON exactly as the API returned it
```

Save a result to disk — `save()` streams the file, follows redirects and never sends your API
key to the file host. Keep it inside the same `with` block (a closed client raises
`ConfigurationError`):

```python
from fastsaver import FastSaver

with FastSaver() as client:
    result = client.fetch("https://www.instagram.com/p/DRsmm9UjKfH/")

    if result.is_album:
        # Carousels have no top-level download_url — save every item
        for i, url in enumerate(result.download_urls, 1):
            client.save(url, f"downloads/{result.id}_{i}")
    elif result.download_url:
        # Pick your own file name ...
        path = client.save(result.download_url, f"downloads/{result.id}.mp4")

        # ... or hand it a directory (created on demand) and let the response name the file
        path = client.save(result.download_url, "downloads/")
        print(path)
```

The remaining sections assume one open client created for the whole program:

```python
from fastsaver import FastSaver

client = FastSaver()  # reads FASTSAVER_API_KEY; call client.close() at exit
```

> Ready to run this? **[Get a free API key](https://api.fastsaver.io/auth)** — 1,000 credits
> (about 660 Instagram/TikTok fetches), no card.

## Async client and bulk downloads

`AsyncFastSaver` has the same endpoint methods — just `await` them (close it with
`await client.aclose()` or `async with`). Reuse one instance per application; it is safe to use
from many coroutines at once.

```python
import asyncio
from fastsaver import AsyncFastSaver, FastSaverError

URLS = [
    "https://www.instagram.com/reel/DRsmm9UjKfH/",
    "https://www.tiktok.com/@scout2015/video/6718335390845095173",
    "https://pin.it/4nUh8Jx",
]

REQUESTS_PER_MINUTE = 10   # Free plan; Pro 60, Ultra 250, Mega 900 (see client.balance().rpm_limit)

async def main() -> None:
    # Rate limits are counted per API key in a FIXED 60-second window and the API sends no
    # Retry-After, so space requests out client-side instead of firing everything at once.
    # A semaphore alone only limits concurrency, not requests per minute.
    gap = 60 / REQUESTS_PER_MINUTE
    lock = asyncio.Lock()

    async with AsyncFastSaver() as client:

        async def fetch(url: str):
            async with lock:            # one request every `gap` seconds
                await asyncio.sleep(gap)
            return await client.fetch(url)

        results = await asyncio.gather(*(fetch(u) for u in URLS), return_exceptions=True)
        for url, r in zip(URLS, results):
            if isinstance(r, FastSaverError):
                print("failed:", url, r)
            else:
                print(r.source, r.type, r.download_urls)
                for link in r.download_urls:
                    await client.save(link, "downloads/")

asyncio.run(main())
```

`await client.save(url, dest)` works the same way as the sync version (the network part is
async; disk writes are ordinary blocking writes). Downloads from the CDN do not count against
the API rate limit — only `fetch`, `youtube.*` and `shazam.*` calls do.

## Instagram downloader API (reels, posts, stories)

An Instagram reels downloader in Python is one call: `client.fetch()` handles reels, posts,
carousels, IGTV, stories and highlights. Every post form works: `instagram.com/p/…`, `/reel/…`,
`/tv/…`, `/share/…` links and `/stories/<user>/<story_id>/`.

```python
reel = client.fetch("https://www.instagram.com/reel/DRsmm9UjKfH/")
print(reel.type, reel.download_url, reel.thumbnail_url, reel.duration)

post = client.fetch("https://www.instagram.com/p/C1abcDEfGhI/")
if post.is_album:
    # carousel: one file per slide (items carry their own type/size)
    for i, item in enumerate(post.items, 1):
        if item.download_url:
            ext = "mp4" if item.is_video else "jpg"
            client.save(item.download_url, f"downloads/{post.id}_{i}.{ext}")
elif post.download_url:
    client.save(post.download_url, "downloads/")   # single image or video, named by the response

story = client.fetch("https://www.instagram.com/stories/nasa/3141592653589793238/")  # 5 credits
```

Notes: a private or deleted post raises `NotFoundError` / `FetchError` (0.1 credit). Profile
URLs are rejected — pass a post, reel or story link.

## TikTok downloader API (no watermark)

A TikTok video downloader API without watermark: videos come back **clean, no watermark**, photo
posts as albums, and every result carries the original sound:

```python
tt = client.fetch("https://www.tiktok.com/@scout2015/video/6718335390845095173")
print(tt.download_url)           # clean MP4, no watermark
print(tt.width, tt.height, tt.duration, tt.caption)

if tt.music:
    print(tt.music.title, tt.music.author, tt.music.download_url)   # MP3 of the sound
```

Short `vm.tiktok.com/…` and `vt.tiktok.com/…` links are accepted too.

## YouTube to MP4 and MP3 download API (up to 4K)

```python
# Download a YouTube video in Python without yt-dlp or ffmpeg: metadata + available formats.
# Accepts watch, youtu.be, shorts, embed, live URLs or a bare id.
info = client.youtube.info("https://www.youtube.com/watch?v=HanBb8FonWs")
print(info.title, info.author, info.duration)   # duration in seconds
print(info.available_formats)    # ['audio', '144p', '360p', '720p', '1080p', ...]
print(info.best_video_format)    # '1080p'
for f in info.formats:
    print(f.type, f.format, f.filesize)

# YouTube Music search: 10 results per page, pages 1-3
search = client.youtube.search("the weeknd one of the girls", page=1)
for track in search:
    # track.duration is a "m:ss" string; use track.duration_seconds for an int
    print(track.video_id, track.title, track.duration, track.url)

# Download as MP4 ("144p" ... "2160p") ...
dl = client.youtube.download("https://youtu.be/HanBb8FonWs", format="720p")   # 15 credits
print(dl.filename, dl.download_url)

# ... or as audio/MP3 ("audio" — "mp3"/"m4a" are aliases)
mp3 = client.youtube.download("https://youtu.be/HanBb8FonWs", format="audio")  # 15 credits

# The link is valid for 10 minutes — save it right away
if dl.download_url:
    path = client.save(dl.download_url, "downloads/")   # file name comes from the response
    print(path)
```

- `download_url` needs no API key and redirects to the actual file. It expires after
  **10 minutes** (it can be reused until then); an expired link raises `DownloadError` from
  `save()`.
- An invalid `format` raises `InvalidArgumentError` before any credits are spent.
- Conversion happens upstream, so a download can take a while — the SDK uses a 120 s read
  timeout for this call. Override per call with `timeout=`.

## Telegram music bot: YouTube audio as a `file_id`

The killer feature for a Telegram music bot in Python. `audio_for_bot` returns a **Telegram
`file_id`** for the audio of a YouTube video, so your bot calls `send_audio` without downloading,
converting or uploading anything — no 50 MB upload limit, no bandwidth, no ffmpeg. Millions of
tracks are pre-cached: a cache hit costs 7 credits and returns in seconds; a miss is downloaded
first (15 credits, may take a minute or more — the default read timeout for this call is 300 s).

```python
audio = client.youtube.audio_for_bot("vk6014HuxcE", "@my_music_bot")
print(audio.file_id)

# Prefer the channel/message pair? (for copy_message-style delivery)
ref = client.youtube.audio_for_bot("vk6014HuxcE", "@my_music_bot", return_file_id=False)
print(ref.channel_id, ref.message_id)
```

> A `file_id` is **bot-scoped**: it only works for the bot you pass as `bot_username` (the
> leading `@` is added for you). Store the `file_id` — re-sending it later is a plain
> Telegram call that costs no credits.

Minimal [aiogram 3](https://docs.aiogram.dev) handler (`pip install "aiogram>=3"`). `BOT_USERNAME`
**must be the username of the bot whose token you pass to `Bot(...)`** — a `file_id` only works
for that bot:

```python
import os

from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command

from fastsaver import AsyncFastSaver, FastSaverError

BOT_TOKEN = os.environ["BOT_TOKEN"]          # from @BotFather
BOT_USERNAME = os.environ["BOT_USERNAME"]    # "@my_music_bot" — the SAME bot as BOT_TOKEN

bot = Bot(BOT_TOKEN)
dp = Dispatcher()
saver = AsyncFastSaver()  # reads FASTSAVER_API_KEY

@dp.message(Command("song"))
async def song(message: types.Message) -> None:
    query = (message.text or "").partition(" ")[2].strip()
    if not query:
        await message.answer("Usage: /song <title or artist>")
        return
    try:
        search = await saver.youtube.search(query)              # 2 credits
        track = next((t for t in search if t.video_id), None)
        if track is None or track.video_id is None:
            await message.answer("Nothing found.")
            return
        audio = await saver.youtube.audio_for_bot(track.video_id, BOT_USERNAME)  # 7 / 15 credits
    except FastSaverError as exc:
        await message.answer(f"Could not fetch audio: {exc}")
        return
    if audio.file_id is None:
        await message.answer("No file_id returned for this track.")
        return
    await message.answer_audio(audio.file_id, title=track.title)  # no download, no upload

dp.run_polling(bot)
```

A complete bot lives in
[`examples/telegram_bot_aiogram.py`](https://github.com/coder2077/fastsaver/blob/main/examples/telegram_bot_aiogram.py).

> Building a Telegram music bot? The Free plan's 1,000 credits cover ~140 cached tracks —
> enough to ship a prototype today: **[api.fastsaver.io/auth](https://api.fastsaver.io/auth)**.
> Pro ($9) covers ~14,000 cached tracks a month.

## Pinterest, Facebook, X/Twitter, RuTube and Likee video downloader

The same `client.fetch(url)` call is a Pinterest video downloader, Facebook reels downloader and
X/Twitter video downloader API, and returns the same `MediaResult`:

```python
pin = client.fetch("https://pin.it/4nUh8Jx")                          # Pinterest (pin.it / pinterest.com)
fb  = client.fetch("https://fb.watch/abc123/")                        # Facebook video / reel
x   = client.fetch("https://x.com/NASA/status/1727080000000000000")   # X / Twitter video, GIF, images
ru  = client.fetch("https://rutube.ru/video/0123456789abcdef/")       # RuTube (3 credits)
lk  = client.fetch("https://l.likee.video/v/AbCdEf")                  # Likee share link (3 credits)

for r in (pin, fb, x, ru, lk):
    print(r.platform, r.type, r.download_urls)
```

Not sure which platform a link belongs to? `detect_platform(url)` mirrors the server's routing
and returns `"youtube"` for links you should send to `client.youtube.*`, `None` for
unsupported hosts, and `"instagram_story"` for story links:

```python
from fastsaver import CREDIT_COSTS, detect_platform

platform = detect_platform("https://vm.tiktok.com/ZMabc/")   # 'tiktok'

if platform is None:
    print("unsupported URL (would cost 0.1 credit if sent)")
elif platform == "youtube":
    print("use client.youtube.info / download / audio_for_bot")
else:
    print(CREDIT_COSTS[platform])   # 1.5 — the /fetch platforms are keys of CREDIT_COSTS
```

## Shazam API for Python: identify songs, top charts, lyrics

```python
# Song recognition API in Python: identify a song from a clip (mp3/m4a/ogg/mp4, up to 50 MB;
# 5-15 s of clear audio is enough)
match = client.shazam.identify("clip.ogg")
print(match.title, "-", match.artist, match.thumbnail)
if match.best_match:
    print("YouTube:", match.best_match.url)   # ready for youtube.download / audio_for_bot

# Top charts: a country code ("us", "gb", "de", ...) or "world"; 10 tracks per page, pages 1-3
for track in client.shazam.top(country="world", page=1):
    print(track.title, track.duration, track.url)

# Lyrics by title + artist (exactly as identify() returned them)
print(client.shazam.lyrics("Blinding Lights", "The Weeknd"))
```

Recognise a **Telegram voice message** (in-memory buffers need a `filename=` so the recogniser
knows the container — Telegram voice notes are OGG/Opus). A complete aiogram 3 handler, using
the same `bot`, `dp`, `saver` and `BOT_USERNAME` as the music-bot example above:

```python
from aiogram import F, types
from fastsaver import FastSaverError

@dp.message(F.voice)
async def recognise(message: types.Message) -> None:
    assert message.voice is not None            # guaranteed by the F.voice filter
    buf = await bot.download(message.voice)     # aiogram: BytesIO
    if buf is None:
        await message.answer("Could not download the voice note.")
        return
    try:
        match = await saver.shazam.identify(buf, filename="voice.ogg")   # 5 credits
    except FastSaverError as exc:
        await message.answer(f"Recognition failed: {exc}")
        return
    if not match.title:
        await message.answer("No match.")
        return
    await message.answer(f"{match.title} — {match.artist}")
    # Deliver the song itself: best YouTube match -> Telegram file_id
    if match.best_match and match.best_match.video_id:
        audio = await saver.youtube.audio_for_bot(match.best_match.video_id, BOT_USERNAME)
        if audio.file_id:
            await message.answer_audio(audio.file_id, title=match.title, performer=match.artist)
```

`file` may be a path, `bytes`, an open binary file, or a `(filename, data[, content_type])`
tuple.

## Balance and usage

`balance()` is free and has its own limit of 60 requests per minute.

```python
b = client.balance()
print(b.plan, b.credits, "/", b.plan_credits)   # Free 987 / 1000
print(b.rpm_limit, b.balance, b.auto_renew)     # 10 0.00 False   (balance is a Decimal, USD)
print(b.plan_expires_at, b.expire_label)        # 2026-10-09 12:00:00+00:00 Ends in 30 days
print(b.is_free_plan)                            # True
```

## Error handling

A call either returns a model (`ok: true`) or raises a subclass of `FastSaverError` — you
never inspect HTTP status codes. Bad arguments (empty URL, unknown `format`, oversized file)
raise `InvalidArgumentError` before any request is made; it is both a `FastSaverError` and a
`ValueError`.

```python
from fastsaver import (
    FastSaver,
    FastSaverError,
    AuthenticationError,
    InsufficientCreditsError,
    RateLimitError,
    NotFoundError,
    FetchError,
)

client = FastSaver()

try:
    result = client.fetch("https://www.instagram.com/p/DRsmm9UjKfH/")
except AuthenticationError:
    print("Invalid API key — check FASTSAVER_API_KEY")
except InsufficientCreditsError:
    print("Out of credits — top up at https://api.fastsaver.io/dashboard")
except RateLimitError:
    # Fixed 60-second window per key and no Retry-After header: wait for the window to roll over
    print("Rate limited — wait up to 60 s (Free: 10 requests/min), then retry")
except NotFoundError:
    print("Post not found (private, deleted or wrong URL)")
except FetchError as exc:
    print("Resolve failed:", exc.code)        # fetch.failed, fetch.timeout, "fetch.error: 502", ...
except FastSaverError as exc:
    print("Something else went wrong:", exc)  # bad argument, network, timeout, 5xx, validation
else:
    print(result.download_urls)
```

```text
FastSaverError
├── ConfigurationError            missing API key, closed client
├── InvalidArgumentError          bad argument, caught client-side (also a ValueError)
├── APIError                      any non-2xx (.status_code, .detail, .body)
│   ├── AuthenticationError       401
│   ├── InsufficientCreditsError  400 "Insufficient credits" — there is no HTTP 402
│   ├── RateLimitError            429 (.retry_after when the server sends Retry-After)
│   ├── InvalidRequestError       400 bad URL / format / bot username / file too large
│   │   └── ValidationError       422 (.errors)
│   ├── FetchError                400 resolve failed (.code, .upstream_status)
│   │   └── NotFoundError         code "not.found"
│   └── ServerError               5xx
├── TransportError                connection problems (wraps httpx errors)
│   └── RequestTimeoutError
└── DownloadError                 save(): final response was not 2xx (e.g. expired link)
```

Billing notes:

- Requests rejected by a guard — bad key, no credits, rate limit, bad format, bad bot username,
  oversized file — cost nothing.
- A resolve that reaches the platform and fails (`FetchError`, `NotFoundError`, and an
  unsupported host on `fetch()`) costs **0.1 credit**.
- Running out of credits is reported as HTTP 400 with `InsufficientCreditsError`, not 402.

## Retries, timeouts, proxies

```python
import httpx
from fastsaver import FastSaver

client = FastSaver(
    max_retries=3,                              # default 2
    timeout=httpx.Timeout(30.0, connect=5.0),   # default Timeout(60.0, connect=10.0)
)

# Per-call override for slow operations
dl = client.youtube.download("https://youtu.be/HanBb8FonWs", format="2160p", timeout=300)
```

- Retried automatically with exponential backoff and jitter: connection errors, HTTP 5xx and
  HTTP 429 (429 backs off 2 s, 4 s; a `Retry-After` header is honoured, capped at 30 s).
- **Not** retried: read timeouts — the server may already have done (and billed) the work. You
  get a `RequestTimeoutError` immediately and decide yourself.
- Default read timeouts: 60 s for most calls, 120 s for `youtube.download`, 300 s for
  `youtube.audio_for_bot` and `save()`. The per-call `timeout=` always wins.
- Rate limits are a fixed 60-second window per key and the API sends no `Retry-After`, so for
  bulk jobs space requests out client-side (see the async example) rather than relying on
  retries.

Bring your own `httpx` client for proxies, custom transports or HTTP/2. The SDK does not close
it, always applies its own timeouts, and never follows redirects on API calls (so your key is
never replayed to another host).

```python
import httpx
from fastsaver import FastSaver, AsyncFastSaver

http = httpx.Client(proxy="http://user:pass@proxy.example:8080")
client = FastSaver(http_client=http)

ahttp = httpx.AsyncClient(http2=True)     # needs: pip install "httpx[http2]"
aclient = AsyncFastSaver(http_client=ahttp)
```

`FastSaver` is thread-safe — create one instance and share it. `AsyncFastSaver` belongs to the
event loop it was created in.

## Command-line interface

The package installs a `fastsaver` command (also `python -m fastsaver`). Output is the raw API
response as indented JSON; pass `--compact` for single-line JSON in pipelines.

```bash
export FASTSAVER_API_KEY="fs_sk_..."

fastsaver fetch "https://www.instagram.com/reel/DRsmm9UjKfH/"
fastsaver youtube info "https://youtu.be/HanBb8FonWs"
fastsaver youtube search "the weeknd" --page 2
fastsaver youtube download "https://youtu.be/HanBb8FonWs" --format 1080p --out ./downloads/
fastsaver youtube tg-audio vk6014HuxcE --bot @my_music_bot [--no-file-id]
fastsaver shazam identify clip.ogg
fastsaver shazam top --country us --page 1
fastsaver shazam lyrics "Blinding Lights" "The Weeknd"
fastsaver balance
fastsaver save "https://api.fastsaver.io/v1/tunnel?id=..." ./downloads/
```

Global options: `--api-key`, `--base-url`, `--timeout`, `--compact`, `--version`. Errors go to
stderr with exit code 1 (2 for usage errors), so the CLI is safe in scripts:

```bash
fastsaver --compact fetch "$URL" | jq -r '.download_url // .items[].download_url'
```

Constants for your own accounting:

```python
from fastsaver import CREDIT_COSTS, YOUTUBE_FORMATS, detect_platform, extract_youtube_id

CREDIT_COSTS["youtube_download"]                   # 15
YOUTUBE_FORMATS                                    # ('audio', '144p', ..., '2160p')
detect_platform("https://vm.tiktok.com/ZMabc/")    # 'tiktok'
extract_youtube_id("https://youtu.be/HanBb8FonWs") # 'HanBb8FonWs'
```

## FAQ

**How do I download an Instagram reel or post with Python?**
`client.fetch("https://www.instagram.com/reel/…")` returns `download_url` (video/image) or
`items` for carousels; `client.save(url, "downloads/")` writes it to disk. Stories and
highlights use the same call.

**How do I download TikTok videos without a watermark in Python?**
`client.fetch(tiktok_url).download_url` is the clean MP4. Photo posts come back as an album,
and `result.music.download_url` is the original sound.

**How do I get a YouTube video as MP4 or MP3 without yt-dlp?**
`client.youtube.download(url, format="1080p")` (or `format="audio"` for MP3/M4A) returns a
link valid for 10 minutes; save it with `client.save()`. Formats go up to 2160p (4K).

**How do I send YouTube music in a Telegram bot without downloading it?**
`client.youtube.audio_for_bot(video_id, "@your_bot")` returns a Telegram `file_id`; pass it
to `send_audio` / `answer_audio`. 7 credits when cached, 15 the first time anyone asks.

**How do I recognise a song from a Telegram voice message?**
`await saver.shazam.identify(buf, filename="voice.ogg")` — 5 credits — returns title, artist,
cover art, lyrics and matching YouTube tracks you can deliver with `audio_for_bot`.

**Is it really free?**
Yes: every account gets 1,000 credits without a card (about 660 TikTok/Instagram fetches, 66
YouTube downloads, or ~140 cached Telegram audio tracks) at 10 requests/minute. Paid plans start
at $9/month for 100,000 credits.

**Do I need Instagram cookies, a login, or proxies?**
No. You send a public post URL and an API key; sessions, proxies and captchas are handled on
FastSaverAPI's side. Nothing runs in a browser on your machine.

**Can it download from private Instagram accounts or a whole profile?**
No. Only public posts, reels, stories and highlights are supported, one URL per call; profile
URLs are rejected. A private or deleted post raises `NotFoundError` / `FetchError` (0.1 credit).

**Can I use it in a commercial app or a public Telegram bot?**
The SDK is MIT-licensed. Use of the API itself is governed by the FastSaverAPI terms at
<https://api.fastsaver.io>; paid plans (Pro/Ultra/Mega) are designed for production bots and
apps, with rate limits from 60 to 900 requests per minute.

**I don't use Python — is there a REST API for Node.js, PHP or Go?**
Yes. This package is a thin client over the plain HTTPS/JSON API documented at
<https://api.fastsaver.io/docs>; every call here maps to one endpoint (`GET /v1/fetch`,
`POST /v1/youtube/download`, `POST /v1/youtube/audio/tg-bot`, `POST /v1/shazam/identify`, …)
with an `X-Api-Key` header.

**Which URLs are supported?**
Instagram (`instagram.com`), TikTok (`tiktok.com`, `vm.tiktok.com`), YouTube (`youtube.com`,
`youtu.be`, shorts, music), Pinterest (`pinterest.com`, `pin.it`), Facebook (`facebook.com`,
`fb.com`, `fb.watch`), X/Twitter (`x.com`, `twitter.com`), RuTube (`rutube.ru`, `rutube.com`),
Likee (`likee.video`, `likee.com`). `detect_platform(url)` tells you before you spend a credit.

**Does the SDK work with asyncio, FastAPI, aiogram, python-telegram-bot?**
Yes — `AsyncFastSaver` is a normal `httpx`-based async client. Create one instance at startup
and reuse it.

**Is my API key safe?**
The key is sent only to `api.fastsaver.io` over HTTPS. `save()` downloads from CDNs without
the key, API calls never follow redirects, and the key is masked in `repr()` and never logged.

**What happens when a post is private or deleted?**
You get `NotFoundError` or `FetchError` (with `.code`) and 0.1 credit is charged for the
attempt. Retrying will not help; guard rejections (bad key, rate limit, no credits) are free.

## Examples

All examples read `FASTSAVER_API_KEY` from the environment and live in
[`examples/`](https://github.com/coder2077/fastsaver/tree/main/examples):

- `quickstart.py` — fetch any supported URL and print its download links.
- `async_batch.py` — download many URLs concurrently, paced to your plan's rate limit, with
  per-URL error reporting.
- `youtube_download.py` — pick a format from `youtube.info`, download, save to disk.
- `telegram_bot_aiogram.py` — a complete music bot: search → `file_id` → `answer_audio`.
- `shazam_identify.py` — identify a local clip, print the best YouTube match and lyrics.

## Support, contributing, license

- **Telegram community & support:** <https://t.me/fastsaverapi>
- **Email:** <support@fastsaver.io>
- **API documentation:** <https://api.fastsaver.io/docs>
- **SDK bugs and feature requests:** [GitHub issues](https://github.com/coder2077/fastsaver/issues)

**Not signed up yet?** [Create a free key](https://api.fastsaver.io/auth) — 1,000 credits, no
card, and [pricing from $9/month](https://api.fastsaver.io/pricing) when you outgrow it.

If this SDK saves you an afternoon, a ⭐ on GitHub helps other developers find it.

Pull requests are welcome — see
[CONTRIBUTING.md](https://github.com/coder2077/fastsaver/blob/main/CONTRIBUTING.md).
Tests are fully mocked with `respx` (800+ test cases, Python 3.9–3.14 in CI), so
`uv sync && uv run pytest` needs no API key. Security issues: see
[SECURITY.md](https://github.com/coder2077/fastsaver/blob/main/SECURITY.md).

The SDK is released under the [MIT License](https://github.com/coder2077/fastsaver/blob/main/LICENSE).
Use of the FastSaverAPI service is subject to its own terms at <https://api.fastsaver.io>.

---

<details>
<summary><b>Русский</b> — Python SDK для FastSaverAPI</summary>

Скачивание видео и фото из Instagram, TikTok (без водяного знака), YouTube (MP4 до 4K и MP3),
Pinterest, Facebook, X/Twitter, RuTube и Likee через один API, распознавание музыки (Shazam) и
аудио из YouTube как Telegram `file_id` для ботов. Sync и async клиенты.
**1 000 кредитов бесплатно, без карты:** <https://api.fastsaver.io/auth>. Поддержка в Telegram:
<https://t.me/fastsaverapi>.

</details>

<details>
<summary><b>O'zbekcha</b> — FastSaverAPI uchun Python SDK</summary>

Instagram, TikTok (suv belgisiz), YouTube (4K gacha MP4 va MP3), Pinterest, Facebook, X/Twitter,
RuTube va Likee'dan video/rasm yuklab olish, Shazam orqali musiqani aniqlash va Telegram botlar
uchun YouTube audioni `file_id` ko'rinishida olish — bitta API. Sync va async.
**1 000 kredit bepul, karta shart emas:** <https://api.fastsaver.io/auth>. Telegram:
<https://t.me/fastsaverapi>.

</details>
