child_process.spawn() doesn't belong in your deployment
The standard Node.js approach to yt-dlp looks like this:
Works locally. Fails the moment you deploy to Lambda or Cloudflare Workers, which don't run arbitrary binaries. And even on a VPS that has the binary, you're on the hook for updating it whenever a platform breaks, managing proxies to avoid IP bans, and wiring up your own retry logic.
The alternative is one fetch() call.
Setup
Node.js 18+ has built-in fetch. No extra dependencies. Older versions can drop in node-fetch.
Basic download
Usage:
The output key follows the input key: in_video → out_video, in_clip → out_clip. Whatever you put after in_ becomes the out_ key in the response.
Download from any platform
Same endpoint, any yt-dlp-supported URL:
Tagging jobs with metadata
Pass up to 10 key/value pairs in metadata to track which user triggered the download, or link it back to your own job system:
Metadata comes back in poll responses and webhooks.
Download and process in one call
If you need to transcode the video right after downloading it, use /run-ytdlp-command. It downloads first, then runs your FFmpeg command on the result:
{{double_braces}} for placeholders, output_files maps each output key to a filename. More recipes in the download and process guide.
Full client class
TypeScript
Serverless
No binary to bundle, so this works on any platform that can make HTTP requests:
Cloudflare Workers:
AWS Lambda:
FAQ
Do I need to keep yt-dlp updated?
No. Binary updates, extraction fixes, and platform changes all happen on our end.
What if a download fails?
The poll response returns status: "FAILED" with an error field. Usually the video is private, the URL is wrong, or the platform rate-limited the request. For the last one, retry after a few seconds.
Can I submit multiple downloads at once?
Yes — fire off multiple requests in parallel, each gets its own command_id. Check X-RateLimit-Remaining in response headers before large batches.