DEV Community

Javid Jamae
Javid Jamae

Posted on • Originally published at ffmpeg-micro.com

How to Call FFmpeg via cURL: Video Processing with a Simple HTTP Request

Originally published at ffmpeg-micro.com

You need to transcode a video. You know cURL. You don't want to install FFmpeg, wrestle with codec flags, or spin up a processing server.

Good news: you can process video with a single cURL command by hitting the FFmpeg Micro API. Upload a file, kick off a transcode, and download the result. All from your terminal.

This guide walks through every step with copy-paste cURL commands.

Get Your API Key

Sign up at ffmpeg-micro.com and grab your API key from the dashboard. Every request needs it as a Bearer token:

export FFMPEG_API_KEY="your-api-key-here"
Enter fullscreen mode Exit fullscreen mode

You get free processing minutes on the starter tier, so you can test everything below without paying a cent.

Upload a Video File via cURL

FFmpeg Micro uses a three-step upload flow: get a presigned URL, upload the file directly, then confirm.

Step 1: Request a presigned upload URL

curl -X POST https://api.ffmpeg-micro.com/v1/upload/presigned-url \
  -H "Authorization: Bearer $FFMPEG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "input.mp4", "contentType": "video/mp4", "fileSize": 50000000}'
Enter fullscreen mode Exit fullscreen mode

This returns an uploadUrl (a signed Google Cloud Storage URL) and a filename you'll use in the next steps.

Step 2: Upload the file

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary @input.mp4
Enter fullscreen mode Exit fullscreen mode

Replace $UPLOAD_URL with the uploadUrl from the previous response. This uploads directly to cloud storage, so even large files transfer quickly.

Step 3: Confirm the upload

curl -X POST https://api.ffmpeg-micro.com/v1/upload/confirm \
  -H "Authorization: Bearer $FFMPEG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "input.mp4", "fileSize": 50000000}'
Enter fullscreen mode Exit fullscreen mode

The response includes a fileUrl (a gs:// URI). Save this. You'll pass it to the transcode endpoint.

Transcode Video with a cURL Command

Now the fun part. Send a POST to /v1/transcodes with your file URL and the output format you want.

Simple mode: use a preset

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer $FFMPEG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{"url": "gs://your-bucket/input.mp4"}],
    "outputFormat": "webm",
    "preset": {"quality": "high", "resolution": "1080p"}
  }'
Enter fullscreen mode Exit fullscreen mode

Replace the gs:// URL with the fileUrl from the upload confirm step. Quality options are low, medium, and high. Resolution options are 480p, 720p, 1080p, and 4k.

The response includes an id field. That's your job ID.

Advanced mode: pass custom FFmpeg options

If you need fine-grained control, skip the preset and pass raw FFmpeg options:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer $FFMPEG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{"url": "gs://your-bucket/input.mp4"}],
    "outputFormat": "mp4",
    "options": [
      {"option": "-c:v", "argument": "libx264"},
      {"option": "-crf", "argument": "18"},
      {"option": "-c:a", "argument": "aac"}
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

This gives you the full power of FFmpeg without actually running FFmpeg. The API validates your options and runs them on cloud infrastructure that scales automatically.

Check Job Status via cURL

Transcoding takes a few seconds to a few minutes depending on file size. Poll the status:

curl https://api.ffmpeg-micro.com/v1/transcodes/$JOB_ID \
  -H "Authorization: Bearer $FFMPEG_API_KEY"
Enter fullscreen mode Exit fullscreen mode

You'll see status change from queued to processing to completed. The response also includes progress (0-100) so you can build a progress bar if you want.

Download the Processed Video

Once the job status is completed, grab your output:

curl https://api.ffmpeg-micro.com/v1/transcodes/$JOB_ID/download \
  -H "Authorization: Bearer $FFMPEG_API_KEY"
Enter fullscreen mode Exit fullscreen mode

This returns a signed download URL. Use it to fetch the file:

curl -L -o output.webm "$DOWNLOAD_URL"
Enter fullscreen mode Exit fullscreen mode

The signed URL expires after a set period, so download promptly or request a new one.

Supported Formats for cURL FFmpeg Requests

FFmpeg Micro handles the same formats FFmpeg does, through a clean API:

Type Formats
Video MP4, WebM, AVI, MOV, MKV, FLV
Audio MP3, M4A, AAC, WAV, OGG, Opus, FLAC
Images JPEG, PNG, GIF, WebP

You can convert between any of these by changing the outputFormat field in your transcode request.

Full cURL Script: Upload, Transcode, Download

Here's a complete script that uploads a local video, transcodes it to WebM, waits for completion, and downloads the result:

#!/bin/bash
set -e

API="https://api.ffmpeg-micro.com"
AUTH="Authorization: Bearer $FFMPEG_API_KEY"
FILE="input.mp4"

# 1. Get presigned upload URL
PRESIGNED=$(curl -s -X POST "$API/v1/upload/presigned-url" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d "{\"filename\": \"$FILE\", \"contentType\": \"video/mp4\", \"fileSize\": $(stat -f%z "$FILE")}")
UPLOAD_URL=$(echo "$PRESIGNED" | python3 -c "import sys,json; print(json.load(sys.stdin)['uploadUrl'])")

# 2. Upload
curl -s -X PUT "$UPLOAD_URL" -H "Content-Type: video/mp4" --data-binary @"$FILE"

# 3. Confirm
CONFIRMED=$(curl -s -X POST "$API/v1/upload/confirm" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d "{\"filename\": \"$FILE\", \"fileSize\": $(stat -f%z "$FILE")}")
FILE_URL=$(echo "$CONFIRMED" | python3 -c "import sys,json; print(json.load(sys.stdin)['fileUrl'])")

# 4. Transcode
JOB=$(curl -s -X POST "$API/v1/transcodes" \
  -H "$AUTH" -H "Content-Type: application/json" \
  -d "{\"inputs\": [{\"url\": \"$FILE_URL\"}], \"outputFormat\": \"webm\", \"preset\": {\"quality\": \"high\", \"resolution\": \"1080p\"}}")
JOB_ID=$(echo "$JOB" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

# 5. Poll until complete
while true; do
  STATUS=$(curl -s "$API/v1/transcodes/$JOB_ID" -H "$AUTH" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "Status: $STATUS"
  [ "$STATUS" = "completed" ] && break
  [ "$STATUS" = "failed" ] && echo "Job failed" && exit 1
  sleep 5
done

# 6. Download
DL_URL=$(curl -s "$API/v1/transcodes/$JOB_ID/download" -H "$AUTH" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['url'])")
curl -L -o output.webm "$DL_URL"
echo "Done: output.webm"
Enter fullscreen mode Exit fullscreen mode

About 30 lines of bash to go from a local MP4 to a transcoded WebM. No FFmpeg installation, no server configuration, no dependency management.

FAQ

Can I use FFmpeg Micro without uploading first?

Yes. If your video is already at a public HTTP/HTTPS URL, pass that URL directly in the inputs array instead of using the upload flow. The API fetches it for you.

What happens if my transcode job fails?

The status endpoint returns failed with an error message explaining what went wrong. Common causes are unsupported codecs or corrupted input files. You don't get charged for failed jobs.

Is there a file size limit?

The free tier has limits on processing minutes rather than file size. Check the pricing page for current tier limits.

Can I cancel a running job?

Yes. Send a PATCH to /v1/transcodes/$JOB_ID/cancel with your Bearer token. The job stops and you're only charged for the processing time used.

How is this different from running FFmpeg locally?

You skip the installation, the codec dependency hell, the scaling problems, and the server maintenance. The tradeoff is network latency on upload/download. For batch processing or apps that need video features without infrastructure, the API approach saves serious engineering time.

Sign up for FFmpeg Micro and try the cURL commands above with your own video. The free tier gives you enough minutes to test the full workflow.

Top comments (0)