Skip to content

DocsRaw ffmpeg

Migrating from your own ffmpeg

Send the ffmpeg commands you already run as jobs, by naming files with aliases, and move to JSON one job at a time.

If your service already runs ffmpeg, you do not need to rewrite anything to start. Send each command as a job: the same arguments, with the files named by aliases instead of paths. When a job is easier to say in JSON, switch that one job; both kinds are the same Job resource, with the same webhooks, usage and rate limits.

Turn a command into a job#

Take the command you run today:

bash
ffmpeg \
  -i talk.mp4 \
  -vf scale=-2:720 \
  -c:v libx264 -crf 23 \
  -c:a copy \
  talk-720p.mp4
  1. Drop ffmpeg itself, and -y, -nostdin and -hide_banner, which are not supported in a command.
  2. Give each input an alias in_<name> in input_files, mapped to an http or https URL or a completed upload, and write {{in_<name>}} where the path was.
  3. Give each output an alias out_<name> in output_files, mapped to its file name, and write {{out_<name>}} where the path was. The extension picks the format.
bash
curl "$VIDEXO_API_URL/v1/jobs" \
  -H "Authorization: Bearer $VIDEXO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "input_files": {
    "in_video": "https://videxo.io/samples/talk.mp4"
  },
  "output_files": {
    "out_main": "talk-720p.mp4"
  },
  "command": "-i {{in_video}} -vf scale=-2:720 -c:v libx264 -crf 23 -c:a copy {{out_main}}",
  "metadata": {
    "order": "1234"
  }
}'

The API returns the job like any other. ffmpeg_command puts the URLs and file names back, so you can run the same command on your own machine to compare:

JSON
{
  "id": "job_3k9x0c2mfq8w1n7r5t4b6v2z",
  "state": "queued",
  "kind": "command",
  "operation": null,
  "spec": null,
  "command": "-i {{in_video}} -vf scale=-2:720 -c:v libx264 -crf 23 -c:a copy {{out_main}}",
  "input_files": {
    "in_video": "https://videxo.io/samples/talk.mp4"
  },
  "output_files": {
    "out_main": "talk-720p.mp4"
  },
  "ffmpeg_command": "ffmpeg -i https://videxo.io/samples/talk.mp4 -vf scale=-2:720 -c:v libx264 -crf 23 -c:a copy talk-720p.mp4",
  "steps": [
    {
      "tool": "ffmpeg",
      "inputs": [
        "video"
      ],
      "outputs": [
        "main"
      ],
      "command": "ffmpeg -i https://videxo.io/samples/talk.mp4 -vf scale=-2:720 -c:v libx264 -crf 23 -c:a copy talk-720p.mp4"
    }
  ],
  "webhook_url": null,
  "metadata": {
    "order": "1234"
  },
  "progress": null,
  "error": null,
  "outputs": [],
  "usage": null,
  "created_at": "2026-11-04T12:00:00.000Z",
  "started_at": null,
  "finished_at": null
}

What changes#

  • One ffmpeg run. Pipes, redirects, variables and globs are not supported.
  • Files by alias. Paths, URLs and protocols inside the command are not supported. Every alias in the maps must appear in the command.
  • Supported options. Check that the command uses supported options, codecs, formats and filters (command rules). Otherwise POST /v1/jobs returns 400 command_invalid with a code for each problem, and no job is created.
  • Limits. Your plan sets the longest job, the size of inputs and outputs and how many run at once (Limits per plan).

The MCP tool check_command validates a command without creating a job, and needs no API key (MCP server).

Moving a job to JSON#

Most jobs that resize, compress, cut, watermark or grab frames say the same thing shorter in JSON, and the API keeps the ffmpeg details, such as even sizes for H.264 and seeking each part of an input, for you. The command above as JSON:

bash
curl "$VIDEXO_API_URL/v1/transcode" \
  -H "Authorization: Bearer $VIDEXO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "input": "https://example.com/talk.mp4",
  "filename": "talk-720p.mp4",
  "resize": {
    "height": 720
  },
  "audio": {
    "codec": "copy"
  }
}'

The ffmpeg tab shows the command the API runs for it. It is not character for character your own: the API writes every choice out, such as the codec, the pixel format and +faststart for MP4. Operations lists every option and the guides show them by task.

Keep a command for what JSON does not cover; the command rules are the limit of both.