FFmpeg REST API Tutorial: First Video in 5 Minutes

February 15, 2026 · RenderIO

Five minutes from zero to converted video

Most FFmpeg tutorials start with installing FFmpeg. This one doesn't. You'll convert a video using a REST API in under 5 minutes. No binary to install. No server to configure.

Here's what we'll do:

  1. Get an API key

  2. Submit an FFmpeg command

  3. Poll for the result

  4. Download the output

Three HTTP requests. That's the whole thing.

The problem with local FFmpeg

The typical FFmpeg experience: install it, spend 30 minutes debugging codec support, figure out the right flags, and get your video transcoding working. Then you deploy.

In production, you discover that your server's FFmpeg build doesn't support libx264 because it wasn't compiled with --enable-gpl. Or your Docker image is 800MB because FFmpeg pulls in every library. Or your Lambda function times out because video encoding takes longer than 15 minutes.

A REST API skips all of that. You send a command string. You get a processed file back.

Step 1: Get your API key

Sign up at renderio.dev. Plans start at $9/mo.

Navigate to the Dashboard and create an API key. It'll look like this:

ffsk_a1b2c3d4e5f6...

Store it somewhere safe. You'll need it for every request.

Step 2: Convert a video with curl

Let's convert a sample MP4 to WebM format:

curl -X POST https://renderio.dev/api/v1/run-ffmpeg-command \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: ffsk_your_api_key_here" \
  -d '{
    "ffmpeg_command": "-i {{in_video}} -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus {{out_video}}",
    "input_files": {
      "in_video": "https://sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4"
    },
    "output_files": {
      "out_video": "output.webm"
    }
  }'

The response returns immediately:

{
  "command_id": "cmd_x7k9m2",
  "status": "processing"
}

The video is being processed in an isolated container. Your terminal isn't blocked. Your server isn't busy.

Step 3: Poll for the result

Use the command_id to check status:

curl https://renderio.dev/api/v1/commands/cmd_x7k9m2 \
  -H "X-API-KEY: ffsk_your_api_key_here"

While processing:

{
  "command_id": "cmd_x7k9m2",
  "status": "processing"
}

When complete:

{
  "command_id": "cmd_x7k9m2",
  "status": "SUCCESS",
  "output_files": {
    "out_video": "https://media.renderio.dev/output.webm"
  }
}

Step 4: Download the output

The output_files URLs are pre-signed. Download directly:

curl -o output.webm "https://media.renderio.dev/output.webm"

Four commands total. The video processed on RenderIO's servers — nothing ran locally.

Full Python example

Here's a complete Python script with polling and error handling:

import requests
import time

API_KEY = "ffsk_your_api_key_here"
BASE_URL = "https://renderio.dev/api/v1"
HEADERS = {
    "Content-Type": "application/json",
    "X-API-KEY": API_KEY
}

# Submit the command
response = requests.post(f"{BASE_URL}/run-ffmpeg-command", headers=HEADERS, json={
    "ffmpeg_command": "-i {{in_video}} -c:v libx264 -preset fast -crf 22 -c:a aac {{out_video}}",
    "input_files": {
        "in_video": "https://example.com/source.mov"
    },
    "output_files": {
        "out_video": "converted.mp4"
    }
})

data = response.json()
command_id = data["command_id"]
print(f"Command submitted: {command_id}")

# Poll for completion
while True:
    status_response = requests.get(
        f"{BASE_URL}/commands/{command_id}",
        headers=HEADERS
    )
    status_data = status_response.json()

    if status_data["status"] == "completed":
        print(f"Done! Output: {status_data['output_files']['out_video']}")
        break
    elif status_data["status"] == "failed":
        print(f"Failed: {status_data.get('error', 'Unknown error')}")
        break

    print("Processing...")
    time.sleep(2)

# Download the result
if status_data["status"] == "completed":
    output_url = status_data["output_files"]["out_video"]
    file_response = requests.get(output_url)
    with open("converted.mp4", "wb") as f:
        f.write(file_response.content)
    print("Downloaded converted.mp4")

Run it:

python convert.py
# Command submitted: cmd_x7k9m2
# Processing...
# Processing...
# Done! Output: https://media.renderio.dev/converted.mp4
# Downloaded converted.mp4

Full Node.js example

Same operation in JavaScript:

const API_KEY = "ffsk_your_api_key_here";
const BASE_URL = "https://renderio.dev/api/v1";

async function convertVideo() {
  // Submit the command
  const submitResponse = await fetch(`${BASE_URL}/run-ffmpeg-command`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-KEY": API_KEY,
    },
    body: JSON.stringify({
      ffmpeg_command: "-i {{in_video}} -c:v libx264 -preset fast -crf 22 -c:a aac {{out_video}}",
      input_files: {
        in_video: "https://example.com/source.mov",
      },
      output_files: {
        out_video: "converted.mp4",
      },
    }),
  });

  const { command_id } = await submitResponse.json();
  console.log(`Command submitted: ${command_id}`);

  // Poll for completion
  while (true) {
    const statusResponse = await fetch(`${BASE_URL}/commands/${command_id}`, {
      headers: { "X-API-KEY": API_KEY },
    });
    const statusData = await statusResponse.json();

    if (statusData.status === "SUCCESS") {
      console.log(`Done! Output: ${statusData.output_files.out_video.storage_url}`);
      return statusData.output_files.out_video.storage_url;
    }

    if (statusData.status === "FAILED") {
      throw new Error(statusData.error || "Processing failed");
    }

    console.log("Processing...");
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }
}

convertVideo().catch(console.error);

Handling errors

Three things can go wrong:

  1. Bad API key: You get a 401. Check your key.

  2. Invalid command: You get a 400 with details about what's wrong in the FFmpeg syntax.

  3. Processing failure: The command runs but FFmpeg errors out. Status becomes failed with an error message.

Always check the status. Always handle the failed case:

if status_data["status"] == "failed":
    error = status_data.get("error", "Unknown")
    print(f"FFmpeg error: {error}")
    # Common: wrong codec, invalid input URL, bad filter syntax

What to try next

Now that you've converted a video, try these operations:

Resize to 720p:

-i {{in_video}} -vf scale=-1:720 -c:v libx264 -c:a copy {{out_video}}

Extract first frame as thumbnail:

-i {{in_video}} -vframes 1 -q:v 2 {{out_image}}

Trim to first 30 seconds (see the full trim guide for frame-accurate cutting and batch operations):

-i {{in_video}} -t 30 -c copy {{out_video}}

Extract audio:

-i {{in_video}} -vn -acodec libmp3lame -q:a 2 {{out_audio}}

Each of these works the same way: change the ffmpeg_command, adjust the output extension, submit, poll, download.

For 20 more ready-to-use examples, see the curl examples. The FFmpeg cheat sheet covers 50 commands with explanations.

Common mistakes

A few things that trip people up on their first integration:

Forgetting the X-API-KEY header on poll requests. The submit call works, but polling returns 401. Both endpoints need the key.

Using local file paths instead of URLs. The API can't access files on your machine. Upload to S3, R2, or any public URL first, then pass the URL as input.

Not handling the failed status. If your FFmpeg command has a syntax error (wrong filter name, missing codec), the job will fail. Always check for "status": "FAILED" and read the error message.

Expecting instant results. Short videos finish in seconds, but a 1GB file might take a couple minutes. Set your polling interval to 2-3 seconds and add a reasonable timeout.

FAQ

Is this the same as running FFmpeg locally?

The commands are identical. If you have a working FFmpeg command on your machine, wrap it in the API format (replace file paths with {placeholders}) and it runs the same way. RenderIO uses FFmpeg 7.x with all major codecs enabled.

How do I upload files that aren't publicly accessible?

Upload to a storage service first (S3, R2, Google Cloud Storage) and use a pre-signed URL. The API fetches the file from that URL before processing.

Can I chain multiple FFmpeg commands?

Yes. RenderIO supports chained commands where the output of one step feeds into the next. See the complete API guide for the chained commands endpoint.

What's the maximum file size?

The API handles files up to several GB. Processing time scales with file size, so for very large files, consider trimming or splitting first.

Do I need to worry about FFmpeg version compatibility?

No. RenderIO runs a consistent FFmpeg 7.x build with all codecs. You don't have to think about --enable-gpl or missing libraries.

Start building

The Starter plan at $9/mo includes 500 commands. Get your API key to start converting video. For language-specific guides, see the Python and Node.js tutorials.