yt-dlp is a headache to run in production
Running yt-dlp on your own machine is fine. Running it as part of a real service is something else.
Platforms change their extraction logic all the time, so you're constantly chasing yt-dlp updates just to stay functional. You need residential or rotating proxies or you'll get IP-banned. Serverless platforms can't run binaries at all. And all the retry and rate-limit logic falls on you.
The RenderIO yt-dlp API handles that stack. You POST a URL, you get back a file. Here's how.
What the API does
The API accepts a public video URL, downloads it via yt-dlp, and stores the result. It supports YouTube, TikTok, Instagram, Twitter/X, Reddit, Vimeo, Twitch, and 1,000+ other platforms.
Private videos, DRM-protected content, and anything behind a login wall return an error — public only.
Two endpoints:
POST /api/v1/ytdlp-download— download, no post-processingPOST /api/v1/run-ytdlp-command— download, then optionally run an FFmpeg command on the result
Both are async: submit, get a command_id, poll for the result.
Your first download
You get back:
Poll for status:
When it finishes:
The file URL is at output_files.out_video.storage_url. The output key is derived from your input key: in_video → out_video, in_clip → out_clip.
The input_urls key format
Keys must start with in_. Whatever comes after the underscore becomes the out_ key in the response.
→ output_files.out_clip.storage_url
Pick a name that makes sense for your code. in_video, in_clip, in_source — all valid.
Polling
Status values: QUEUED, PROCESSING, SUCCESS, FAILED.
Most downloads wrap up in 10–60 seconds. Larger files take longer, as you'd expect.
Metadata
Pass up to 10 key/value pairs in metadata to tag the job with your own identifiers — user IDs, job references, whatever you need for correlation later.
It comes back in poll responses and webhooks.
Download and process in one call
Need to transcode the video right after downloading? Use /api/v1/run-ytdlp-command instead. It downloads first, then runs your FFmpeg command on the result.
{{double_braces}} for placeholders. output_files maps each output key to a filename. Skip ffmpeg_command entirely and it behaves like the plain download endpoint.
More recipes in the download and process guide.
When things fail
If the URL is unavailable or unsupported, the poll response returns status: "FAILED":
The usual culprits: the video was deleted or is private, the platform requires a login, the URL format isn't something yt-dlp recognizes, or the platform temporarily rate-limited the request (retry usually works for the last one).
Rate limits
Your plan determines the limit. Check X-RateLimit-Remaining in response headers before batching a lot of requests.
Language-specific guides
Full examples with polling and error handling for:
Node.js — fetch-based, works on any serverless platform
Python — requests-based with a complete client class
Supported platforms
| Platform | Notes |
| YouTube | Public videos, Shorts, playlists (one URL per call) |
| TikTok | Public posts only |
| Public posts and Reels | |
| Twitter/X | Public tweets with video |
| Video posts | |
| Vimeo | Public videos |
| Twitch | VODs and clips |
| Public videos |
Age-gated content and anything requiring a login won't work.
FAQ
Does this support downloading entire YouTube playlists?
No — one URL per request. For playlists, iterate over the individual video URLs and submit separately.
What format does the downloaded video come in?
yt-dlp picks the best available format, usually MP4 with H.264 video and AAC audio. If you need something specific, use the run-ytdlp-command endpoint and add an FFmpeg transcode step.
Can I call this from a serverless function?
Yes. It's just HTTP. Works from Lambda, Cloudflare Workers, Vercel — anything with outbound requests.