RuTube downloader API without a proxy or a VPN
The RuTube downloader API resolves any public rutube.ru video with a single GET request — a direct video URL, thumbnail, duration and caption as JSON, for 3 credits a call. The geo-sensitive part of reaching RuTube happens on our infrastructure, so your workers run wherever you like. It is the same call you already make for Instagram or TikTok.
- Endpoint
GET /fetch- Credits
- 3 per link, 0.1 if it fails
- Auth
X-Api-Keyheader- Returns
- direct media URL + metadata
audience
who actually needs this
RuTube is where much of the Russian-speaking internet watches video, and it is nearly invisible in tooling built elsewhere. If your product has users across Russia, Belarus, Kazakhstan or the wider CIS, RuTube links will arrive whether you planned for them or not.
- Telegram bots and chat apps — users paste whatever they were watching; a bot that shrugs looks broken.
- Aggregators and schedulers — mirroring a creator's uploads breaks when one platform is unreachable.
- Archival and monitoring tools — research and newsrooms need the file, not a page that may vanish.
- Localised products — for many apps RuTube is the platform their audience uses, not a long-tail extra.
None of that is exotic — the same product shapes people build on Instagram or TikTok, for an audience that happens to be elsewhere.
the hard part
why RuTube is awkward to fetch yourself
RuTube does not hand you an MP4. Video arrives as a segmented HLS stream, and access to the playlist is geo-sensitive: the same code run from a US or EU cloud region gets a different answer, sometimes none at all. That is an infrastructure problem, not a parsing problem — which is why most general-purpose downloaders list every platform except this one.
We absorb it: egress, retries, segment handling and the churn when the player changes. What
crosses the wire to you is a plain HTTPS URL you can GET from anywhere. One caveat —
do not hardcode or pattern-match that URL's hostname. It is not guaranteed to be the same on
the next request.
usage
one GET, one link
No SDK, no body, no per-platform branch. You pass the video link and read the JSON.
https://api.fastsaver.io/v1/fetch
3 credits
curl "https://api.fastsaver.io/v1/fetch?url=https%3A%2F%2Frutube.ru%2Fvideo%2Fd1c0a7f0b4e94a2c8f5b6d3e1a7c9b02%2F" \
-H "X-Api-Key: fs_sk_•••••••••••"
{
"ok": true,
"id": "d1c0a7f0b4e94a2c8f5b6d3e1a7c9b02",
"source": "rutube.ru",
"type": "video",
"download_url": "https://...",
"thumbnail_url": "https://...",
"width": null,
"height": null,
"duration": 843,
"caption": "Тест-драйв нового кроссовера"
}
Those two nulls are not a quirk of this example: a RuTube video response never carries pixel dimensions. Everything else is populated on a normal public video — and a RuTube photo post answers with a shorter object than this one, which the field notes below spell out.
Percent-encode the url value, as above. RuTube share links often carry a tracking
query string, and an unescaped & reads as the start of your next parameter — the
request still returns 200, just for the wrong link.
reference
the four fields that behave differently here
The envelope is the shared one: ok, source,
thumbnail_url, duration and caption mean exactly what they
mean on every other platform, and the
Instagram downloader API page documents them row by row.
Rather than repeat that table, here is only what a RuTube link does differently.
| Field | Type | On RuTube |
|---|---|---|
id | string | RuTube's own 32-character hex identifier — the one in the /video/…/ path. Use it as the primary key in your cache. |
type | string | video on a normal upload, image on a RuTube photo post. Never album — there is no items array to walk, so the branch you wrote for Instagram carousels never fires here — but do not hardcode video either. |
download_url | string | A plain HTTPS URL you can GET from any region — not a RuTube CDN link. Time-limited, and the hostname can change between calls. On an image response it points at the picture itself. |
width / height | null | Always null on a video, and absent altogether on an image. RuTube never reports pixel dimensions, so size a player from the file or the poster image instead. |
That image case is uncommon, but it is the one shape that changes which keys exist
rather than which values they hold. A photo post carries download_url,
thumbnail_url — the same URL, there is no separate poster — and caption,
and no width, height or duration at all. Not null: missing.
Code that reads meta["duration"] straight out of the object raises on it.
{
"ok": true,
"id": "c4f21a8e7b3d40559ae6182fbb7c0d31",
"source": "rutube.ru",
"type": "image",
"download_url": "https://...",
"thumbnail_url": "https://...",
"caption": "Кадр со съёмок"
}
Read everything except ok and type defensively. Branch on
type first, then let coverage vary upload to upload — a missing caption is normal,
not a failure. Use .get() with a default rather than indexing straight into the
object.
integration
adding RuTube to code you already wrote
There is no RuTube client, no RuTube-shaped response, no second code path. If your service already
calls /fetch, support here is a hostname in a set. Below is a complete resolve-and-save
worker; the RuTube-specific part is one line.
import requests
from urllib.parse import urlparse
FETCH = "https://api.fastsaver.io/v1/fetch"
HEADERS = {"X-Api-Key": "fs_sk_•••••••••••"}
SUPPORTED = {
"instagram.com", "tiktok.com", "pinterest.com",
"x.com", "facebook.com",
"rutube.ru", # <- the entire change
}
def host_of(link):
return urlparse(link).netloc.removeprefix("www.").lower()
def save(link, path):
host = host_of(link)
if host not in SUPPORTED:
raise ValueError("unsupported host: " + host)
meta = requests.get(FETCH, params={"url": link},
headers=HEADERS, timeout=90).json()
if not meta.get("ok"):
raise RuntimeError(meta.get("detail", "fetch failed"))
# Stream to disk — a 40-minute upload will not fit comfortably in memory.
with requests.get(meta["download_url"], stream=True, timeout=300) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1 << 16):
f.write(chunk)
return meta
save("https://rutube.ru/video/d1c0a7f0b4e94a2c8f5b6d3e1a7c9b02/", "clip.mp4")
Or, if you just want the file locally:
URL=$(curl -s -G "https://api.fastsaver.io/v1/fetch" \
--data-urlencode "url=https://rutube.ru/video/d1c0a7f0b4e94a2c8f5b6d3e1a7c9b02/" \
-H "X-Api-Key: $FASTSAVER_KEY" | jq -r '.download_url')
curl -L -o clip.mp4 "$URL"
curl -G --data-urlencode does the escaping for you — the most common cause of a
confusing empty result, gone.
honesty
what RuTube will not give you
- Public videos only. Login walls, age gates, paid content and rights-holder blocks return an error. No parameter unlocks them.
- No dimensions.
widthandheightcome backnullon every RuTube video and are absent from animageresponse. Neither shape tells you the pixel size; if your UI needs it, read it off the downloaded file. - The download URL expires. Resolve and transfer in the same job. Re-resolving costs another 3 credits and still beats a dead link.
- A failure is not free. Private, deleted or malformed links charge 0.1 credits each — 1/30th of a successful resolve, but budget for it if you replay a queue of links that never worked.
- Files are large. RuTube hosts long-form video; forty minutes at 1080p runs to hundreds of megabytes. Stream to storage, set generous timeouts.
- Throughput is capped per plan — 10 requests per minute on Free, 900 on Mega. On RuTube you will usually saturate your own transfer workers long before you see a
429. - Platforms move. When RuTube changes its player, we ship the fix behind the same endpoint. Your integration stays put.
What you do with the file is your responsibility. A download is not a licence — respect the uploader's rights, the platform's terms and your local copyright law. Archiving for research and republishing someone's video are different acts.
faq
questions about the RuTube API
Do I need a Russian proxy or a VPN to download RuTube videos?
No — that is the part we take on. Reaching RuTube is a networking problem: where a request originates changes what the platform returns. Your worker can sit in Frankfurt or Ohio and get the same JSON.
Does this need a RuTube account, token or cookies?
No. The only credential is your own X-Api-Key header. No OAuth dance, no session to keep warm, nothing to renew when RuTube rotates something.
What does one RuTube video cost?
3 credits — twice what a TikTok, Pinterest, X or Facebook link costs, and the difference pays for the geo-sensitive fetch described above. On Pro that is roughly 33,000 videos a month; the free tier covers about 333. Failures are cheap but not free: every failed resolve — private video, dead link, malformed URL — charges a flat 0.1 credits, so a retry loop over links that will never work still bills you. Checking /balance is free.
How long is the download_url usable?
Long enough to download, not long enough to store. Treat it as a one-shot handle: resolve, transfer, discard. Need the video next week? Call /fetch again — cheaper than debugging a link that died in your database.
Can I download private, restricted or paid RuTube videos?
No. The API resolves what an anonymous visitor can watch. Anything behind a login, an age gate, a purchase or a rights-holder block returns an error, and no parameter changes that.
Resolve a RuTube link right now
Paste a rutube.ru URL into the playground and look at the JSON before you commit to anything.