Complete guide to using the FFMPEG Video Processing API
The FFMPEG Video Merger API provides seven powerful video processing capabilities:
Create videos by combining static images with audio files
Concatenate multiple videos from URLs into one seamless video
Overlay one video on top of another with customizable positioning
Burn ASS subtitle files directly into videos
Split audio files into equal parts with customizable segments
Trim audio files to exact durations with precise timing
Convert horizontal videos to vertical format (3:4 or 9:16)
https://ffmpegapi.net/api/
If you want to use AI Tools like Cursor, you can download this README file to your project and ask your AI agent to use it as reference.
All API endpoints require authentication using API keys. You can provide your API key in three ways:
X-API-Key: your_api_key_here
?api_key=your_api_key_here
api_key=your_api_key_here
For long-running video processing tasks that may take several minutes, you can use async processing to avoid timeouts and improve user experience.
"async": true
to your request JSON to process jobs in the background. You'll receive a job_id
immediately and can check progress using the job status endpoint.
Status | Description |
---|---|
pending | Job is queued and waiting to be processed |
processing | Job is currently being processed |
completed | Job finished successfully, download URL available |
failed | Job failed, error message available |
/api/merge_image_audio
Create a video by combining a static image with an audio file from URLs. The image will be displayed for the duration of the audio. Supports async processing for large files.
Content-Type: application/json
Parameter | Type | Required | Description |
---|---|---|---|
image |
String | Yes | URL to image file (PNG, JPG, JPEG) |
audio |
String | Yes | URL to audio file (MP3, WAV, M4A) |
async |
Boolean | No | Process job asynchronously (default: false) |
{
"image": "https://example.com/image.jpg",
"audio": "https://example.com/audio.mp3",
"async": true
}
{
"success": true,
"message": "Video created successfully",
"download_url": "https://ffmpegapi.net/download/filename.mp4",
"filename": "unique_filename.mp4"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
/api/merge_videos
Concatenate multiple videos from URLs into a single video. Videos with different aspect ratios can be handled by specifying output dimensions.
Content-Type: application/json
Parameter | Type | Required | Description |
---|---|---|---|
video_urls |
Array | Yes | Array of video URLs (minimum 2 required) |
audio_url |
String | No | Optional audio URL to replace original audio |
subtitle_url |
String | No | Optional ASS subtitle file URL to burn into the merged video |
watermark_url |
String | No | Optional image URL to add as a watermark in the bottom right corner (auto-scaled to 30% of video width) |
dimensions |
String | No | Output video dimensions (e.g., "864x480"). All videos will be scaled/padded to match these dimensions. Useful for merging videos with different aspect ratios. |
async |
Boolean | No | Process job asynchronously (default: false) |
{
"video_urls": [
"https://example.com/video1.mp4",
"https://example.com/video2.mp4",
"https://example.com/video3.mp4"
],
"audio_url": "https://example.com/audio.mp3",
"subtitle_url": "https://example.com/subtitles.ass",
"watermark_url": "https://example.com/watermark.png",
"dimensions": "1920x1080",
"async": true
}
{
"success": true,
"message": "Videos merged successfully",
"download_url": "https://ffmpegapi.net/download/merged_filename.mp4",
"filename": "unique_merged_filename.mp4"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
/api/picture_in_picture
Create a picture-in-picture video by overlaying one video on top of another with customizable positioning and scaling.
Content-Type: application/json
Parameter | Type | Required | Description |
---|---|---|---|
main_video_url |
String | Yes | URL of the main background video |
pip_video_url |
String | Yes | URL of the video to overlay (picture-in-picture) |
position |
String | No | Position of overlay. Options: top-left, top-center, top-right, middle-left, middle, middle-right, bottom-left, bottom-center, bottom-right. Default: bottom-right |
scale |
String | No | Scale of overlay video. Options: iw/4:ih/4 (quarter), iw/3:ih/3 (third), iw/2:ih/2 (half). Default: iw/4:ih/4 |
audio_option |
String | No | Audio source for final video. Options: video1 (main video audio), video2 (overlay video audio), mute (no audio). Default: video1 |
async |
Boolean | No | Process job asynchronously (default: false) |
{
"main_video_url": "https://example.com/main-video.mp4",
"pip_video_url": "https://example.com/overlay-video.mp4",
"position": "top-right",
"scale": "iw/3:ih/3",
"audio_option": "video2",
"async": true
}
{
"success": true,
"message": "Picture-in-picture video created successfully",
"download_url": "https://ffmpegapi.net/download/pip_filename.mp4",
"filename": "unique_pip_filename.mp4"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
Endpoint: /api/add_subtitles
Burns ASS subtitle files directly into videos using FFmpeg, creating a permanent overlay of subtitles.
Parameter | Type | Required | Description |
---|---|---|---|
video_url |
string | Yes | Direct URL to the video file |
subtitle_url |
string | Yes | Direct URL to the ASS subtitle file |
async |
boolean | No | Process in background (default: false) |
curl -X POST https://ffmpegapi.net/api/add_subtitles \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://example.com/video.mp4",
"subtitle_url": "https://example.com/subtitles.ass"
}'
{
"success": true,
"message": "Subtitles added successfully!",
"filename": "uuid_subtitled_video.mp4",
"download_url": "https://ffmpegapi.net/api/storage/uuid_subtitled_video.mp4"
}
/api/split_audio
Split an audio file into equal parts. Perfect for creating audio segments, podcast chapters, or breaking large audio files into smaller chunks.
Content-Type: application/json
Parameter | Type | Required | Description |
---|---|---|---|
audio_url |
String | Yes | URL of the audio file to split (MP3, WAV, AAC, etc.) |
parts |
Integer | Yes | Number of equal parts to split into (2-20) |
async |
Boolean | No | Process asynchronously (default: false) |
curl -X POST "https://ffmpegapi.net/api/split_audio" \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"audio_url": "https://example.com/audio.mp3",
"parts": 4
}'
{
"success": true,
"message": "Audio successfully split into 4 parts",
"parts": 4,
"audio_parts": [
{
"part": "split_part_01.mp3",
"download_url": "https://ffmpegapi.net/download/uuid/split_part_01.mp3"
},
{
"part": "split_part_02.mp3",
"download_url": "https://ffmpegapi.net/download/uuid/split_part_02.mp3"
},
{
"part": "split_part_03.mp3",
"download_url": "https://ffmpegapi.net/download/uuid/split_part_03.mp3"
},
{
"part": "split_part_04.mp3",
"download_url": "https://ffmpegapi.net/download/uuid/split_part_04.mp3"
}
]
}
curl -X POST "https://ffmpegapi.net/api/split_audio" \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"audio_url": "https://example.com/long_audio.mp3",
"parts": 8,
"async": true
}'
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
/api/trim_audio
Trim an audio file to a specific duration. Perfect for creating shorter audio clips, removing unwanted sections, or preparing audio for video overlay.
Parameter | Type | Required | Description |
---|---|---|---|
audio_url |
string | Yes | URL of the audio file to trim (supports most audio formats) |
desired_length |
number | Yes | Desired length in seconds (supports decimal values, e.g., 90.5) |
fade_duration |
number | No | Duration of fade-out effect in seconds (default: 0, no fade) |
curl -X POST "https://ffmpegapi.net/api/trim_audio" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"audio_url": "https://example.com/audio.mp3",
"desired_length": 120.5,
"fade_duration": 10
}'
{
"success": true,
"filename": "trimmed_audio_1694022400.mp3",
"download_url": "https://ffmpegapi.net/api/storage/trimmed_audio_1694022400.mp3",
"trimmed_length": 120.5,
"fade_duration": 10,
"original_length": 240.0,
"message": "Audio trimmed successfully with fade effect"
}
{
"success": false,
"error": "Audio file duration (30.0 seconds) is shorter than desired length (120.5 seconds)"
}
curl -X POST "https://ffmpegapi.net/api/trim_audio" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"audio_url": "https://example.com/podcast_episode.mp3",
"desired_length": 180,
"fade_duration": 10
}'
import requests
import json
url = "https://ffmpegapi.net/api/trim_audio"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
data = {
"audio_url": "https://example.com/podcast_episode.mp3",
"desired_length": 180,
"fade_duration": 10
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if result["success"]:
print(f"Audio trimmed successfully!")
print(f"Download URL: {result['download_url']}")
print(f"Trimmed to: {result['trimmed_length']} seconds")
if result.get('fade_duration', 0) > 0:
print(f"Fade effect: {result['fade_duration']} seconds")
else:
print(f"Error: {result['error']}")
const trimAudio = async () => {
const response = await fetch('https://ffmpegapi.net/api/trim_audio', {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
audio_url: 'https://example.com/podcast_episode.mp3',
desired_length: 180,
fade_duration: 10
})
});
const result = await response.json();
if (result.success) {
console.log('Audio trimmed successfully!');
console.log('Download URL:', result.download_url);
console.log('Trimmed to:', result.trimmed_length, 'seconds');
if (result.fade_duration > 0) {
console.log('Fade effect:', result.fade_duration, 'seconds');
}
} else {
console.error('Error:', result.error);
}
};
trimAudio();
'https://example.com/podcast_episode.mp3',
'desired_length' => 180,
'fade_duration' => 10
);
$options = array(
'http' => array(
'header' => "X-API-Key: YOUR_API_KEY\r\n" .
"Content-Type: application/json\r\n",
'method' => 'POST',
'content' => json_encode($data)
)
);
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
$result = json_decode($response, true);
if ($result['success']) {
echo "Audio trimmed successfully!\n";
echo "Download URL: " . $result['download_url'] . "\n";
echo "Trimmed to: " . $result['trimmed_length'] . " seconds\n";
if ($result['fade_duration'] > 0) {
echo "Fade effect: " . $result['fade_duration'] . " seconds\n";
}
} else {
echo "Error: " . $result['error'] . "\n";
}
?>
When async processing is enabled for your account, you'll receive:
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
/api/convert_to_vertical
Convert horizontal videos to vertical format optimized for mobile viewing. Automatically selects the best aspect ratio (3:4 or 9:16) based on the original video dimensions, with optional watermark placement in the top right corner.
Content-Type: application/json
Parameter | Type | Required | Description |
---|---|---|---|
video_url |
String | Yes | URL to the horizontal video file to convert |
watermark_url |
String | No | URL to watermark/logo image (PNG recommended) to overlay in top right corner |
async |
Boolean | No | Process job asynchronously (default: false) |
{
"video_url": "https://example.com/horizontal_video.mp4",
"watermark_url": "https://example.com/logo.png",
"async": true
}
{
"success": true,
"message": "Video successfully converted to vertical 9:16 format",
"download_url": "https://ffmpegapi.net/api/storage/vertical_video.mp4",
"filename": "unique_filename_vertical.mp4"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Job submitted for async processing. Use /api/job/{job_id}/status to check progress.",
"status_url": "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status"
}
The API automatically chooses between 3:4 and 9:16 aspect ratios based on which is closer to your original video's aspect ratio:
The original video is scaled to fit within the target dimensions while maintaining its aspect ratio, with black bars added to fill any remaining space.
curl -X POST "https://ffmpegapi.net/api/convert_to_vertical" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://example.com/video.mp4",
"watermark_url": "https://example.com/logo.png"
}'
import requests
url = "https://ffmpegapi.net/api/convert_to_vertical"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
data = {
"video_url": "https://example.com/video.mp4",
"watermark_url": "https://example.com/logo.png"
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if result["success"]:
print(f"Video converted successfully!")
print(f"Download URL: {result['download_url']}")
print(f"Format: {result['message']}")
else:
print(f"Error: {result['error']}")
const convertVideo = async () => {
const response = await fetch('https://ffmpegapi.net/api/convert_to_vertical', {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
video_url: 'https://example.com/video.mp4',
watermark_url: 'https://example.com/logo.png'
})
});
const result = await response.json();
if (result.success) {
console.log('Video converted successfully!');
console.log('Download URL:', result.download_url);
console.log('Format:', result.message);
} else {
console.error('Error:', result.error);
}
};
convertVideo();
/api/job/{job_id}/status
Check the status of an asynchronous job. Use this endpoint to monitor the progress of jobs submitted with async: true
.
Parameter | Type | Required | Description |
---|---|---|---|
job_id |
String | Yes | Job ID returned when submitting async request |
curl -X GET "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status" \
-H "X-API-Key: your_api_key_here"
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"job_type": "merge_image_audio",
"status": "pending",
"created_at": "2025-08-19T22:00:00.000Z",
"updated_at": "2025-08-19T22:00:00.000Z"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"job_type": "merge_image_audio",
"status": "processing",
"created_at": "2025-08-19T22:00:00.000Z",
"updated_at": "2025-08-19T22:01:30.000Z"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"job_type": "merge_image_audio",
"status": "completed",
"created_at": "2025-08-19T22:00:00.000Z",
"updated_at": "2025-08-19T22:03:45.000Z",
"message": "Video created successfully",
"download_url": "https://ffmpegapi.net/download/filename.mp4",
"filename": "unique_filename.mp4"
}
{
"success": true,
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"job_type": "merge_image_audio",
"status": "failed",
"created_at": "2025-08-19T22:00:00.000Z",
"updated_at": "2025-08-19T22:02:15.000Z",
"error": "Failed to download image: Invalid URL"
}
All endpoints return consistent error responses with appropriate HTTP status codes.
{
"success": false,
"error": "API key is required"
}
{
"success": false,
"error": "Both image and audio files are required"
}
{
"success": false,
"error": "File too large. Maximum file size is 100MB."
}
{
"success": false,
"error": "FFMPEG processing failed: [details]"
}
curl -X POST "https://ffmpegapi.net/api/merge_image_audio" \
-H "X-API-Key: your_api_key_here" \
-F "image=@/path/to/image.jpg" \
-F "audio=@/path/to/audio.mp3"
curl -X POST "https://ffmpegapi.net/api/merge_videos" \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"video_urls": [
"https://example.com/video1.mp4",
"https://example.com/video2.mp4"
]
}'
curl -X POST "https://ffmpegapi.net/api/picture_in_picture" \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key_here" \
-d '{
"main_video_url": "https://example.com/main.mp4",
"pip_video_url": "https://example.com/overlay.mp4",
"position": "top-right",
"scale": "iw/4:ih/4"
}'
import requests
url = "https://ffmpegapi.net/api/merge_image_audio"
headers = {"X-API-Key": "your_api_key_here"}
with open("image.jpg", "rb") as img, open("audio.mp3", "rb") as aud:
files = {
"image": img,
"audio": aud
}
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
result = response.json()
download_url = result["download_url"]
print(f"Video created: {download_url}")
else:
print(f"Error: {response.json()['error']}")
import requests
import json
url = "https://ffmpegapi.net/api/merge_videos"
headers = {
"Content-Type": "application/json",
"X-API-Key": "your_api_key_here"
}
data = {
"video_urls": [
"https://example.com/video1.mp4",
"https://example.com/video2.mp4"
]
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
result = response.json()
download_url = result["download_url"]
print(f"Videos merged: {download_url}")
else:
print(f"Error: {response.json()['error']}")
import requests
url = "https://ffmpegapi.net/api/picture_in_picture"
headers = {
"Content-Type": "application/json",
"X-API-Key": "your_api_key_here"
}
data = {
"main_video_url": "https://example.com/main.mp4",
"pip_video_url": "https://example.com/overlay.mp4",
"position": "top-right",
"scale": "iw/4:ih/4"
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
result = response.json()
download_url = result["download_url"]
print(f"PiP video created: {download_url}")
else:
print(f"Error: {response.json()['error']}")
const formData = new FormData();
formData.append('image', imageFile);
formData.append('audio', audioFile);
const response = await fetch('https://ffmpegapi.net/api/merge_image_audio', {
method: 'POST',
headers: {
'X-API-Key': 'your_api_key_here'
},
body: formData
});
const result = await response.json();
if (result.success) {
console.log('Video created:', result.download_url);
} else {
console.error('Error:', result.error);
}
const data = {
video_urls: [
'https://example.com/video1.mp4',
'https://example.com/video2.mp4'
]
};
const response = await fetch('https://ffmpegapi.net/api/merge_videos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your_api_key_here'
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
console.log('Videos merged:', result.download_url);
} else {
console.error('Error:', result.error);
}
const data = {
main_video_url: 'https://example.com/main.mp4',
pip_video_url: 'https://example.com/overlay.mp4',
position: 'top-right',
scale: 'iw/4:ih/4',
audio_option: 'video1' // Options: 'video1', 'video2', 'mute'
};
const response = await fetch('https://ffmpegapi.net/api/picture_in_picture', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'your_api_key_here'
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
console.log('PiP video created:', result.download_url);
} else {
console.error('Error:', result.error);
}
Need help? Questions about the API? try the web interface first.