FFmpeg Cheat Sheet: Common Commands and the Same Job as an API Call
The FFmpeg commands you use most — convert, compress, resize, trim, extract audio, thumbnails, GIFs, watermarks — each with the same job as one API call.
This cheat sheet lists the FFmpeg commands developers use most. Each entry shows the local command and the same job as one Xora API call, so you can test on your laptop and run the job in the cloud without a change to the logic.
Every Xora example is the JSON body of POST https://api.xora.sh/v1/jobs with the header Authorization: Bearer YOUR_API_KEY. The job returns { id, state } at once; poll GET /v1/jobs/{id} (or pass webhookUrl) to get the output URL. See the quickstart for the full loop.
How to read the Xora examples
- Recipes (
"mode": "recipe") are named jobs for common tasks. You set a few parameters and Xora picks the flags. - Raw args (
"mode": "ffmpeg") take the same arguments you type locally, as a JSON array.{{in_1}}and{{out_1}}are placeholders for the input URL and the output file. Output names use letters, numbers and underscores, plus an extension. - Probe (
"mode": "probe") returns ffprobe metadata and makes no output file.
| Task | Key FFmpeg flags | Xora |
|---|---|---|
| Inspect a file | ffprobe -show_format -show_streams | probe mode |
| Convert MOV to MP4 | -c:v libx264 -c:a aac | transcode |
| Compress | -crf 28 -preset medium | compress |
| Resize | -vf scale=1280:-2 | resize |
| Trim | -ss 30 -t 15 | trim |
| Extract audio | -vn -c:a copy | extractAudio |
| Thumbnail | -ss 5 -frames:v 1 | thumbnail |
| Join clips | -f concat -c copy | concat |
| Web-ready MP4 | -movflags +faststart | webReady |
| GIF | fps=12,scale=480:-1 | raw args |
| Watermark | -filter_complex overlay | raw args |
| Remove audio | -an | raw args |
Inspect a file (ffprobe)
ffprobe -v error -show_format -show_streams -of json input.mp4-of json prints machine-readable output. Run this first when you need the codec, duration or size before you choose a command.
{ "mode": "probe", "input": { "url": "https://example.com/input.mp4" }}The completed job has a probe object with fields such as durationSeconds, width, height, videoCodec and audioCodec. See probe.
Guide: ffprobe API: get video metadata from a URL.
Convert MOV to MP4
ffmpeg -i input.mov -c:v libx264 -crf 23 -c:a aac -b:a 128k output.mp4-c:v libx264 encodes H.264 video, -crf 23 sets the quality, and -c:a aac -b:a 128k encodes AAC audio at 128 kbps. If the MOV already holds H.264 and AAC, -c copy changes only the container and is much faster.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mov" }, "output": { "format": "mp4" }, "recipe": { "name": "transcode" }}Guide: convert MOV to MP4 with FFmpeg.
Compress a video
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset medium -c:a copy output.mp4A higher -crf gives a smaller file. -preset trades encode time for size: slow makes smaller files, fast finishes sooner. -c:a copy keeps the audio unchanged.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "mp4" }, "recipe": { "name": "compress", "crf": 28 }}Guide: how to compress video with FFmpeg.
Resize a video
ffmpeg -i input.mp4 -vf scale=1280:-2 -c:a copy output.mp4scale=1280:-2 sets the width to 1280 pixels and picks a height that keeps the aspect ratio. The -2 keeps the height an even number, which H.264 needs.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "mp4" }, "recipe": { "name": "resize", "width": 1280 }}Guide: how to resize video with FFmpeg.
Trim a clip
# Fast, no re-encode: the cut starts on the nearest keyframeffmpeg -ss 30 -i input.mp4 -t 15 -c copy clip.mp4
# Frame-accurate: re-encodes the clipffmpeg -ss 30 -i input.mp4 -t 15 -c:v libx264 -crf 23 -c:a aac clip.mp4-ss before -i seeks in the input, and -t 15 keeps 15 seconds. Use -to in place of -t to give an end time.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "mp4" }, "recipe": { "name": "trim", "startSeconds": 30, "durationSeconds": 15 }}Guide: how to trim and cut video with FFmpeg.
Extract the audio
# Keep the original audio stream (no quality loss)ffmpeg -i input.mp4 -vn -c:a copy audio.m4a
# Convert to MP3ffmpeg -i input.mp4 -vn -c:a libmp3lame -q:a 2 audio.mp3-vn drops the video. -q:a 2 is a high-quality variable bitrate for MP3.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "mp3" }, "recipe": { "name": "extractAudio" }}The recipe copies the audio stream when it can, so the file you get can have a different extension (for example .aac). output.format is the format it converts to when a copy is not possible.
Guide: extract audio from video with FFmpeg.
Grab a thumbnail
ffmpeg -ss 5 -i input.mp4 -frames:v 1 -q:v 2 thumb.jpg-frames:v 1 writes one frame. -q:v 2 is high JPEG quality (the range is 2 to 31, lower is better).
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "jpg" }, "recipe": { "name": "thumbnail", "seekSeconds": 5 }, "stageInput": false}stageInput: false lets FFmpeg read only the byte ranges it needs, so a frame from a large file does not need a full download. See input staging.
Guide: generate video thumbnails from a URL.
Join clips
# list.txt contains one line per clip: file 'intro.mp4'ffmpeg -f concat -safe 0 -i list.txt -c copy joined.mp4The concat demuxer joins files without a re-encode. All clips must have the same codecs, resolution and frame rate.
{ "mode": "recipe", "input_files": { "in_1": "https://example.com/intro.mp4", "in_2": "https://example.com/main.mp4" }, "output": { "format": "mp4" }, "recipe": { "name": "concat" }}Xora joins the files in key order (in_1, in_2, …), with no list file.
Guide: how to concatenate videos with FFmpeg.
Make an MP4 start playing at once (faststart)
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4+faststart moves the index (the moov atom) to the start of the file, so a browser can start playback before the download ends. With -c copy, nothing is re-encoded.
{ "mode": "recipe", "input": { "url": "https://example.com/input.mp4" }, "output": { "format": "mp4" }, "recipe": { "name": "webReady" }}webReady probes the file first. It passes through a file that is already web-ready, only moves the index when that is all it needs, and re-encodes only when the codecs do not play in browsers.
Make a GIF
ffmpeg -ss 10 -t 3 -i input.mp4 \ -vf "fps=12,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" \ preview.gifpalettegen and paletteuse build a color palette from the clip, which makes the GIF look much better than the default palette.
{ "mode": "ffmpeg", "input_files": { "in_1": "https://example.com/input.mp4" }, "output_files": { "out_1": "preview.gif" }, "ffmpeg": { "args": [ "-ss", "10", "-t", "3", "-i", "{{in_1}}", "-vf", "fps=12,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse", "{{out_1}}" ] }}In the args array, the filter is one element with no shell quotes around it.
Guide: convert video to GIF with FFmpeg.
Add a watermark
ffmpeg -i input.mp4 -i logo.png \ -filter_complex "[0:v][1:v]overlay=W-w-24:H-h-24" \ -c:v libx264 -crf 23 -c:a copy branded.mp4overlay=W-w-24:H-h-24 puts the logo 24 pixels from the bottom-right corner. W and H are the video size; w and h are the logo size.
{ "mode": "ffmpeg", "input_files": { "in_1": "https://example.com/input.mp4", "in_2": "https://example.com/logo.png" }, "output_files": { "out_1": "branded.mp4" }, "ffmpeg": { "args": [ "-i", "{{in_1}}", "-i", "{{in_2}}", "-filter_complex", "[0:v][1:v]overlay=W-w-24:H-h-24", "-c:v", "libx264", "-crf", "23", "-c:a", "copy", "{{out_1}}" ] }}For text watermarks, see programmatic watermarks.
Remove the audio
ffmpeg -i input.mp4 -c:v copy -an silent.mp4-an drops all audio streams, and -c:v copy keeps the video unchanged.
{ "mode": "ffmpeg", "input_files": { "in_1": "https://example.com/input.mp4" }, "output_files": { "out_1": "silent.mp4" }, "ffmpeg": { "args": ["-i", "{{in_1}}", "-c:v", "copy", "-an", "{{out_1}}"] }}Common errors
| Error | Usual cause | Fix |
|---|---|---|
height not divisible by 2 | A scale filter gave an odd size for H.264. | Use -2 in place of -1 for the auto side. |
Unknown encoder 'libx264' | The local FFmpeg build has no x264. | Install a full build, or run the job on Xora. |
moov atom not found | The input file is incomplete or not an MP4. | Download the file again; probe it first. |
Black or frozen start after -c copy trim | The cut did not start on a keyframe. | Re-encode the clip, or move the start to a keyframe. |
VALIDATION_ERROR (Xora) | A field in the job body is wrong. | Read error.message; it names the field. |
Xora returns errors as JSON with a code, and failed or rejected jobs cost nothing. See error handling.
More
- How to run FFmpeg in the cloud: VM, Docker, serverless or API.
- Custom FFmpeg args: the full rules for raw mode.
- FFmpeg for AI agents: let an agent write and run these commands over MCP.
Frequently asked questions
What is the basic FFmpeg command format?
ffmpeg [input options] -i input [output options] output. Options before -i apply to the input (for example -ss to seek), and options after it apply to the output (for example -c:v libx264 to choose the video codec).
How do I copy streams without re-encoding?
Use -c copy. FFmpeg then moves the audio and video streams into the new file unchanged. It is fast and keeps the quality, but it cannot change the codec, size or frame rate, and cuts can only start on keyframes.
What CRF value should I use for H.264?
Lower CRF means higher quality and bigger files. 18 to 28 is the usual range for libx264, and 23 is the default. Try 23 first, then go up to make the file smaller or down to keep more detail.
Can I run these FFmpeg commands without installing FFmpeg?
Yes. Send the same arguments to the Xora API as a JSON array in mode ffmpeg, or use a recipe for the common jobs. Xora runs FFmpeg in the cloud and returns a signed URL for the output.