Your First Command
Step-by-step guide to submitting an FFmpeg command, polling for results, and downloading output files.
Your First Command
This guide walks you through submitting an FFmpeg command, polling for results, and downloading output files. Before you begin, make sure you have your API key ready.
RenderIO processes FFmpeg commands in three steps:
- Submit -- POST your command with input and output file mappings
- Poll -- Check the command status until it completes
- Download -- Retrieve your output files from the storage URLs
Step 1: Submit a command
Send a POST request to the /api/v1/run-ffmpeg-command endpoint.
Request body
{
"input_files": {
"in_video": "https://example.com/sample.mp4"
},
"output_files": {
"out_video": "converted.webm"
},
"ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}",
"metadata": {
"project": "marketing",
"task": "web-optimization"
},
"webhook_url": "https://your-server.com/webhooks/renderio"
}Field reference
| Field | Type | Required | Description |
|---|---|---|---|
input_files | object | Yes | Map of aliases to source URLs. Each key must start with in_ (e.g. in_video, in_audio, in_watermark). Values are publicly accessible URLs to your source files. |
output_files | object | Yes | Map of aliases to output filenames. Each key must start with out_ (e.g. out_video, out_thumbnail). Values are the desired filenames for the output. |
ffmpeg_command | string | Yes | The FFmpeg command to execute. Use {{alias}} placeholders that match your input and output file keys. RenderIO replaces them with actual file paths at runtime. |
metadata | object | No | Arbitrary key-value pairs (up to 10 properties) attached to the command. Returned in poll responses and webhooks. Useful for tracking jobs in your system. |
webhook_url | string | No | A URL to receive a POST request when this specific command completes. Overrides any account-level webhook configuration for this command. |
Alias rules
- Input file keys must start with
in_(e.g.in_video,in_audio) - Output file keys must start with
out_(e.g.out_video,out_thumb) - Keys must be alphanumeric with underscores only (
[a-zA-Z0-9_]) - Use the same alias names in your
ffmpeg_commandwrapped in double curly braces:{{in_video}},{{out_video}}
Response
The API returns immediately with a command_id:
{
"command_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}The FFmpeg processing runs asynchronously in the background. Your client does not need to keep the connection open.
Step 2: Poll for status
Send a GET request to /api/v1/commands/:commandId to check the status:
curl https://renderio.dev/api/v1/commands/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "X-API-KEY: ffsk_your_api_key_here"Command statuses
| Status | Description |
|---|---|
QUEUED | Command has been received and is waiting to be processed |
PROCESSING | FFmpeg is currently running |
SUCCESS | Command completed successfully, output files are available |
FAILED | Command failed, check error_status and error_message for details |
Poll response (SUCCESS)
{
"command_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "SUCCESS",
"command_type": "FFMPEG_COMMAND",
"total_processing_seconds": 4.21,
"ffmpeg_command_run_seconds": 2.87,
"metadata": {
"project": "marketing",
"task": "web-optimization"
},
"original_request": {
"input_files": {
"in_video": "https://example.com/sample.mp4"
},
"output_files": {
"out_video": "converted.webm"
},
"ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}"
},
"output_files": {
"out_video": {
"file_id": "f1a2b3c4-d5e6-7890-abcd-ef1234567890",
"storage_url": "https://storage.renderio.dev/files/f1a2b3c4...",
"status": "STORED",
"rendi_store_type": "OUTPUT",
"is_deleted": false,
"filename": "converted.webm",
"size_mbytes": 2.34,
"duration": 15.0,
"file_type": "video",
"file_format": "webm",
"codec": "vp9",
"mime_type": "video/webm",
"width": 1920,
"height": 1080,
"frame_rate": 30,
"bitrate_video_kb": 1280
}
}
}Poll response (FAILED)
{
"command_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "FAILED",
"command_type": "FFMPEG_COMMAND",
"error_status": "FFMPEG_ERROR",
"error_message": "Invalid codec specified",
"original_request": {
"input_files": { "in_video": "https://example.com/sample.mp4" },
"output_files": { "out_video": "converted.webm" },
"ffmpeg_command": "ffmpeg -i {{in_video}} -c:v invalid_codec {{out_video}}"
}
}Step 3: Download output files
Each entry in output_files includes a storage_url. Use it to download the file directly:
curl -o converted.webm "https://storage.renderio.dev/files/f1a2b3c4..."The storage_url is a pre-signed URL that provides temporary access to your file.
Full code examples
# Submit
COMMAND_ID=$(curl -s -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
-H "Content-Type: application/json" \
-H "X-API-KEY: $RENDERIO_API_KEY" \
-d '{
"input_files": { "in_video": "https://example.com/sample.mp4" },
"output_files": { "out_video": "converted.webm" },
"ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}"
}' | jq -r '.command_id')
# Poll
while true; do
RESULT=$(curl -s https://renderio.dev/api/v1/commands/$COMMAND_ID \
-H "X-API-KEY: $RENDERIO_API_KEY")
STATUS=$(echo $RESULT | jq -r '.status')
echo "Status: $STATUS"
if [ "$STATUS" = "SUCCESS" ] || [ "$STATUS" = "FAILED" ]; then break; fi
sleep 2
done
# Download
DOWNLOAD_URL=$(echo $RESULT | jq -r '.output_files.out_video.storage_url')
curl -o converted.webm "$DOWNLOAD_URL"import os
import time
import requests
API_KEY = os.environ["RENDERIO_API_KEY"]
BASE_URL = "https://api.renderio.dev"
# 1. Submit the command
submit_response = requests.post(
f"{BASE_URL}/api/v1/run-ffmpeg-command",
headers={
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
},
json={
"input_files": {"in_video": "https://example.com/sample.mp4"},
"output_files": {"out_video": "converted.webm"},
"ffmpeg_command": "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}",
},
)
command_id = submit_response.json()["command_id"]
print(f"Command ID: {command_id}")
# 2. Poll for results
while True:
poll_response = requests.get(
f"{BASE_URL}/api/v1/commands/{command_id}",
headers={"X-API-KEY": API_KEY},
)
result = poll_response.json()
print(f"Status: {result['status']}")
if result["status"] in ("SUCCESS", "FAILED"):
break
time.sleep(2)
# 3. Download the output
if result["status"] == "SUCCESS":
file_url = result["output_files"]["out_video"]["storage_url"]
print(f"Download URL: {file_url}")
download = requests.get(file_url)
with open("converted.webm", "wb") as f:
f.write(download.content)
print("File saved to converted.webm")const API_KEY: string = process.env.RENDERIO_API_KEY!;
const BASE_URL = "https://api.renderio.dev";
interface SubmitResponse {
command_id: string;
}
interface OutputFile {
file_id: string;
storage_url: string;
status: string;
filename: string;
size_mbytes: number;
}
interface CommandResult {
command_id: string;
status: "QUEUED" | "PROCESSING" | "SUCCESS" | "FAILED";
output_files: Record<string, OutputFile>;
}
// 1. Submit the command
const submitResponse = await fetch(`${BASE_URL}/api/v1/run-ffmpeg-command`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
},
body: JSON.stringify({
input_files: { in_video: "https://example.com/sample.mp4" },
output_files: { out_video: "converted.webm" },
ffmpeg_command:
"ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}",
}),
});
const { command_id } = (await submitResponse.json()) as SubmitResponse;
console.log("Command ID:", command_id);
// 2. Poll for results
let result: CommandResult;
while (true) {
const pollResponse = await fetch(
`${BASE_URL}/api/v1/commands/${command_id}`,
{ headers: { "X-API-KEY": API_KEY } },
);
result = (await pollResponse.json()) as CommandResult;
console.log("Status:", result.status);
if (result.status === "SUCCESS" || result.status === "FAILED") break;
await new Promise((resolve) => setTimeout(resolve, 2000));
}
// 3. Download the output
if (result.status === "SUCCESS") {
const fileUrl = result.output_files.out_video.storage_url;
console.log("Download URL:", fileUrl);
}const API_KEY = process.env.RENDERIO_API_KEY;
const BASE_URL = "https://api.renderio.dev";
// 1. Submit the command
const submitResponse = await fetch(`${BASE_URL}/api/v1/run-ffmpeg-command`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": API_KEY,
},
body: JSON.stringify({
input_files: { in_video: "https://example.com/sample.mp4" },
output_files: { out_video: "converted.webm" },
ffmpeg_command:
"ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}",
}),
});
const { command_id } = await submitResponse.json();
console.log("Command ID:", command_id);
// 2. Poll for results
let result;
while (true) {
const pollResponse = await fetch(
`${BASE_URL}/api/v1/commands/${command_id}`,
{ headers: { "X-API-KEY": API_KEY } },
);
result = await pollResponse.json();
console.log("Status:", result.status);
if (result.status === "SUCCESS" || result.status === "FAILED") break;
await new Promise((resolve) => setTimeout(resolve, 2000));
}
// 3. Download the output
if (result.status === "SUCCESS") {
const fileUrl = result.output_files.out_video.storage_url;
console.log("Download URL:", fileUrl);
}<?php
$apiKey = getenv("RENDERIO_API_KEY");
$baseUrl = "https://api.renderio.dev";
// 1. Submit the command
$ch = curl_init("$baseUrl/api/v1/run-ffmpeg-command");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"X-API-KEY: $apiKey",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"input_files" => ["in_video" => "https://example.com/sample.mp4"],
"output_files" => ["out_video" => "converted.webm"],
"ffmpeg_command" => "ffmpeg -i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 {{out_video}}",
]));
$submitResult = json_decode(curl_exec($ch), true);
curl_close($ch);
$commandId = $submitResult["command_id"];
echo "Command ID: $commandId\n";
// 2. Poll for results
while (true) {
$ch = curl_init("$baseUrl/api/v1/commands/$commandId");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["X-API-KEY: $apiKey"]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
echo "Status: " . $result["status"] . "\n";
if (in_array($result["status"], ["SUCCESS", "FAILED"])) break;
sleep(2);
}
// 3. Download the output
if ($result["status"] === "SUCCESS") {
$fileUrl = $result["output_files"]["out_video"]["storage_url"];
echo "Download URL: $fileUrl\n";
file_put_contents("converted.webm", file_get_contents($fileUrl));
echo "File saved to converted.webm\n";
}Next steps
- Polling & Webhooks -- choose between polling and webhook notifications
- Command Types -- learn about chained and batch commands
- Error Handling -- handle HTTP errors and command failures