Download Video (yt-dlp)
Download a publicly accessible video from YouTube, TikTok, Instagram, Reddit, Vimeo, Twitch, and other yt-dlp-supported platforms. Returns a command ID to poll for the download URL.
Download Video (yt-dlp)
POST /api/v1/ytdlp-downloadDownload a publicly accessible video using yt-dlp. RenderIO fetches the video from the source platform, stores it in managed R2 storage, and returns a signed URL. The endpoint returns immediately with a command_id that you poll for the result.
Only public content is supported — no DRM bypass, no private accounts, no paywalled content.
Authentication
Requires API key via X-API-KEY header.
Request
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Content-Type | string | Yes | Must be application/json |
X-API-KEY | string | Yes | Your API key with ffsk_ prefix |
Body
interface YtDlpDownloadRequest {
input_urls: Record<string, string>; // in_* aliases mapped to public video URLs
metadata?: Record<string, string | number | boolean>; // Max 10 keys
}| Field | Type | Required | Description |
|---|---|---|---|
input_urls | Record<string, string> | Yes | Map of alias names (must start with in_) to public video URLs. Each URL is downloaded independently. |
metadata | Record<string, string | number | boolean> | No | Arbitrary key-value metadata attached to the command. Maximum 10 keys. |
This endpoint does not accept ffmpeg_command or output_files. To post-process the downloaded video, use POST /api/v1/run-ytdlp-command instead.
Response
200 OK
{
command_id: string;
}| Field | Type | Description |
|---|---|---|
command_id | string | Unique identifier for the command. Use this to poll for status. |
Getting the downloaded file URL
Poll GET /api/v1/commands/:commandId until status is SUCCESS, then access the file at:
result.output_files.out_<suffix>.storage_urlThe downloaded file is keyed as out_<suffix> where <suffix> matches your in_<suffix> input key. For example, in_video → output_files.out_video.storage_url.
Error responses
| Status | Error | Description |
|---|---|---|
400 | INVALID_REQUEST | Missing input_urls, invalid in_ key prefix, or ffmpeg_command/output_files included (not allowed on this endpoint). |
401 | UNAUTHORIZED | Missing or invalid API key. |
422 | VALIDATION_ERROR | Input validation failed (e.g., metadata exceeds 10 keys, invalid URL). |
429 | RATE_LIMITED | Too many requests. Retry after the period indicated in the Retry-After header. |
501 | NOT_IMPLEMENTED | yt-dlp backend unavailable. Retry after a short delay. |
Examples
curl -X POST https://renderio.dev/api/v1/ytdlp-download \
-H "Content-Type: application/json" \
-H "X-API-KEY: ffsk_your_api_key_here" \
-d '{
"input_urls": {
"in_video": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
},
"metadata": {
"source": "youtube",
"project": "archive"
}
}'import os, time, requests
API_KEY = os.environ["RENDERIO_API_KEY"]
BASE = "https://renderio.dev"
# Submit download
res = requests.post(
f"{BASE}/api/v1/ytdlp-download",
headers={"X-API-KEY": API_KEY, "Content-Type": "application/json"},
json={
"input_urls": {"in_video": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"},
"metadata": {"source": "youtube"},
},
)
res.raise_for_status()
command_id = res.json()["command_id"]
# Poll until done
while True:
time.sleep(2)
result = requests.get(
f"{BASE}/api/v1/commands/{command_id}",
headers={"X-API-KEY": API_KEY},
).json()
if result["status"] == "SUCCESS":
# input key in_video → output key out_video
print(result["output_files"]["out_video"]["storage_url"])
break
if result["status"] == "FAILED":
raise RuntimeError(result.get("error"))const API_KEY = process.env.RENDERIO_API_KEY!;
const BASE = "https://renderio.dev";
interface CommandResult {
command_id: string;
status: "QUEUED" | "PROCESSING" | "SUCCESS" | "FAILED";
output_files: Record<string, { storage_url: string | null; status: string }>;
error?: string;
}
async function downloadVideo(url: string): Promise<string> {
const submitRes = await fetch(`${BASE}/api/v1/ytdlp-download`, {
method: "POST",
headers: { "X-API-KEY": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ input_urls: { in_video: url } }),
});
if (!submitRes.ok) throw new Error(`Submit failed: ${await submitRes.text()}`);
const { command_id } = await submitRes.json();
while (true) {
await new Promise((r) => setTimeout(r, 2000));
const pollRes = await fetch(`${BASE}/api/v1/commands/${command_id}`, {
headers: { "X-API-KEY": API_KEY },
});
const result: CommandResult = await pollRes.json();
// input key in_video → output key out_video
if (result.status === "SUCCESS") return result.output_files.out_video.storage_url!;
if (result.status === "FAILED") throw new Error(result.error);
}
}
const storageUrl = await downloadVideo("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
console.log("Downloaded:", storageUrl);const API_KEY = process.env.RENDERIO_API_KEY;
const BASE = "https://renderio.dev";
async function downloadVideo(url) {
const submitRes = await fetch(`${BASE}/api/v1/ytdlp-download`, {
method: "POST",
headers: { "X-API-KEY": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ input_urls: { in_video: url } }),
});
const { command_id } = await submitRes.json();
while (true) {
await new Promise((r) => setTimeout(r, 2000));
const result = await fetch(`${BASE}/api/v1/commands/${command_id}`, {
headers: { "X-API-KEY": API_KEY },
}).then((r) => r.json());
// input key in_video → output key out_video
if (result.status === "SUCCESS") return result.output_files.out_video.storage_url;
if (result.status === "FAILED") throw new Error(result.error);
}
}
const url = await downloadVideo("https://www.tiktok.com/@username/video/7123456789");
console.log("Downloaded:", url);Supported platforms
RenderIO uses yt-dlp, which supports 1000+ platforms including YouTube, TikTok, Instagram, Twitter/X, Reddit, Vimeo, Twitch, Facebook, and more. See the yt-dlp supported sites list for a full catalog.
Platforms update their APIs frequently. RenderIO patches yt-dlp server-side, so your endpoint and code remain stable even when platforms change their formats.