API Documentation

Complete guide to using the FFMPEG Video Processing API

Overview

The FFMPEG Video Merger API provides seven powerful video processing capabilities:

Image + Audio

Create videos by combining static images with audio files

Video Merge

Concatenate multiple videos from URLs into one seamless video

Picture in Picture

Overlay one video on top of another with customizable positioning

Add Subtitles

Burn ASS subtitle files directly into videos

Split Audio

Split audio files into equal parts with customizable segments

Trim Audio

Trim audio files to exact durations with precise timing

Convert to Vertical

Convert horizontal videos to vertical format (3:4 or 9:16)

Base URL: https://ffmpegapi.net/api/

AI Tools

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.

Tip: This README contains all API endpoints, parameters, curl examples, and sample responses for easy integration with AI coding assistants.

Authentication

All API endpoints require authentication using API keys. You can provide your API key in three ways:

Header (Recommended)
X-API-Key: your_api_key_here
Query Parameter
?api_key=your_api_key_here
Form Data
api_key=your_api_key_here
Need an API Key? Sign up for free to get your API key.

Async Processing

For long-running video processing tasks that may take several minutes, you can use async processing to avoid timeouts and improve user experience.

How it works: Add "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.
Sync Processing (Default)
  • Process immediately
  • Get result in same response
  • May timeout for large files
Async Processing
  • No timeout issues
  • Better for large files
  • Check progress anytime
Job Status Values
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 Endpoints

Image & Audio Merge

POST
/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.

Request Format

Content-Type: application/json

Parameters
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)
Request Example
{
  "image": "https://example.com/image.jpg",
  "audio": "https://example.com/audio.mp3",
  "async": true
}
Sync Response
{
  "success": true,
  "message": "Video created successfully",
  "download_url": "https://ffmpegapi.net/download/filename.mp4",
  "filename": "unique_filename.mp4"
}
Async Response
{
  "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"
}

Video Merge

POST
/api/merge_videos

Concatenate multiple videos from URLs into a single video. Videos with different aspect ratios can be handled by specifying output dimensions.

Request Format

Content-Type: application/json

Parameters
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)
Request Example
{
  "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
}
Sync Response
{
  "success": true,
  "message": "Videos merged successfully",
  "download_url": "https://ffmpegapi.net/download/merged_filename.mp4",
  "filename": "unique_merged_filename.mp4"
}
Async Response
{
  "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"
}

Picture in Picture

POST
/api/picture_in_picture

Create a picture-in-picture video by overlaying one video on top of another with customizable positioning and scaling.

Request Format

Content-Type: application/json

Parameters
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)
Request Example
{
  "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
}
Response
{
  "success": true,
  "message": "Picture-in-picture video created successfully",
  "download_url": "https://ffmpegapi.net/download/pip_filename.mp4",
  "filename": "unique_pip_filename.mp4"
}
Async Response
{
  "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"
}

Add Subtitles

POST

Endpoint: /api/add_subtitles

Burns ASS subtitle files directly into videos using FFmpeg, creating a permanent overlay of subtitles.

Parameters
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)
Example Request
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"
  }'
Response
{
  "success": true,
  "message": "Subtitles added successfully!",
  "filename": "uuid_subtitled_video.mp4",
  "download_url": "https://ffmpegapi.net/api/storage/uuid_subtitled_video.mp4"
}
Note: Only ASS subtitle format is supported. The subtitles are permanently burned into the video and cannot be toggled off.

Split Audio

POST
/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.

Request Format

Content-Type: application/json

Parameters
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)
Request Example
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
  }'
Response Example
{
  "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"
    }
  ]
}
Async Processing Example
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"
}

Trim Audio

POST
/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.

Request Parameters
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)
Example Request
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 Response
{
  "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"
}
Error Response
{
  "success": false,
  "error": "Audio file duration (30.0 seconds) is shorter than desired length (120.5 seconds)"
}
Notes
  • Format Support: Supports MP3, WAV, FLAC, AAC, OGG, and other common audio formats
  • Duration Validation: Desired length cannot exceed the original audio duration
  • Fade Effect: Optional fade-out creates smooth audio endings. Fade duration must be less than total length
  • Fade Calculation: For 60s audio with 10s fade, fade starts at 50s (desired_length - fade_duration)
  • Precision: Supports sub-second precision (e.g., 90.5 seconds)
  • Quality: Output maintains original audio quality and format
  • File Size: Audio file URLs must be publicly accessible and under 100MB
Tip: The trimming always starts from the beginning (00:00) of the audio file and cuts to the specified duration. Use fade_duration=0 for no fade effect, or specify seconds for a smooth fade-out.
Code Examples
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";
}
?>
Async Processing Response

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"
}

Convert to Vertical

POST
/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.

Request Format

Content-Type: application/json

Parameters
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)
Request Example
{
  "video_url": "https://example.com/horizontal_video.mp4",
  "watermark_url": "https://example.com/logo.png",
  "async": true
}
Sync Response
{
  "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"
}
Async Response
{
  "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"
}
Aspect Ratio Selection

The API automatically chooses between 3:4 and 9:16 aspect ratios based on which is closer to your original video's aspect ratio:

  • 3:4 (1080x1440): Selected for wider horizontal videos
  • 9:16 (1080x1920): Selected for narrower horizontal videos (closer to square)

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.

Watermark Details
  • Position: Top right corner with 20px padding
  • Size: Automatically scaled to 20% of video width
  • Format: Supports PNG, JPG, and other common image formats
  • Transparency: PNG transparency is preserved
Tip: Use PNG format with transparency for watermarks to avoid visible background boxes. The watermark will maintain its aspect ratio and be clearly visible without overpowering the video content.
Code Examples
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();

Job Status

GET
/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.

Parameters
Parameter Type Required Description
job_id String Yes Job ID returned when submitting async request
Request Example
curl -X GET "https://ffmpegapi.net/api/job/550e8400-e29b-41d4-a716-446655440000/status" \
  -H "X-API-Key: your_api_key_here"
Response Examples
Pending Job
{
  "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"
}
Processing Job
{
  "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"
}
Completed Job
{
  "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"
}
Failed Job
{
  "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"
}

Error Handling

All endpoints return consistent error responses with appropriate HTTP status codes.

Authentication Error (401)
{
  "success": false,
  "error": "API key is required"
}
Validation Error (400)
{
  "success": false,
  "error": "Both image and audio files are required"
}
File Too Large (413)
{
  "success": false,
  "error": "File too large. Maximum file size is 100MB."
}
Processing Error (500)
{
  "success": false,
  "error": "FFMPEG processing failed: [details]"
}

Code Examples

Image & Audio Merge
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"
Video Merge
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"
    ]
  }'
Picture in Picture
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"
  }'
Image & Audio Merge
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']}")
Video Merge
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']}")
Picture in Picture
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']}")
Image & Audio Merge
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);
}
Video Merge
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);
}
Picture in Picture
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);
}

Rate Limits & Guidelines

Processing Times
  • Image + Audio: 1-5 minutes
  • Video Merge: 2-10 minutes
  • Picture in Picture: 3-15 minutes
Processing time depends on file sizes and complexity
File Limits
  • Max file size: 100MB per file
  • Supported image formats: PNG, JPG, JPEG
  • Supported audio formats: MP3, WAV, M4A
  • Supported video formats: MP4, WebM, MOV
Best Practices: For video merging, ensure all videos have the same resolution and aspect ratio. For picture-in-picture, the overlay video should be smaller than the main video for best results.

Need help? Questions about the API? try the web interface first.