API Documentation

Complete guide to using the FFMPEG Video Processing API

Overview

The FFMPEG Video Merger API provides eight 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

Trim Video

Trim videos by specifying start and end times in seconds

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
curl -X POST "https://ffmpegapi.net/api/merge_image_audio" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "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
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",
      "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
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-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"
}

Split Audio by Segments

POST
/api/split_audio_segments

Split an audio file into segments of a specified duration. Unlike the standard split audio endpoint which divides into equal parts, this endpoint creates segments of a fixed duration. For example, a 66-second audio with segment_duration of 10 seconds will produce 6 segments of 10 seconds each and 1 final segment of 6 seconds.

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.)
segment_duration Number Yes Duration of each segment in seconds (1-3600). Default: 30
async Boolean No Process asynchronously (default: false)
Request Example
curl -X POST "https://ffmpegapi.net/api/split_audio_segments" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "audio_url": "https://example.com/audio.mp3",
    "segment_duration": 10
  }'
Response Example
{
  "success": true,
  "message": "Audio successfully split into 7 segments",
  "segment_duration": 10,
  "total_segments": 7,
  "segments": [
    {
      "segment": "segment_01.mp3",
      "download_url": "https://ffmpegapi.net/download/uuid/segment_01.mp3"
    },
    {
      "segment": "segment_02.mp3", 
      "download_url": "https://ffmpegapi.net/download/uuid/segment_02.mp3"
    },
    {
      "segment": "segment_03.mp3",
      "download_url": "https://ffmpegapi.net/download/uuid/segment_03.mp3"
    }
  ]
}
Note: The last segment may be shorter than the specified duration if the audio length is not evenly divisible by the segment duration. For example, a 66-second audio with 10-second segments will have 6 full segments and 1 segment of 6 seconds.
Async Processing Example
curl -X POST "https://ffmpegapi.net/api/split_audio_segments" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "audio_url": "https://example.com/long_audio.mp3",
    "segment_duration": 30,
    "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"
}

Split Audio by Time

POST
/api/split_audio_time

Extract a specific portion of an audio file by specifying start and end times in milliseconds. Perfect for creating precise audio clips from longer recordings.

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.)
start_time Number Yes Start time in milliseconds (must be non-negative)
end_time Number Yes End time in milliseconds (must be greater than start_time)
async Boolean No Process asynchronously (default: false)
Request Example
curl -X POST "https://ffmpegapi.net/api/split_audio_time" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "audio_url": "https://example.com/audio.mp3",
    "start_time": 5000,
    "end_time": 30000
  }'
Response Example
{
  "success": true,
  "message": "Audio successfully clipped from 5000ms to 30000ms",
  "start_time_ms": 5000,
  "end_time_ms": 30000,
  "duration_ms": 25000,
  "download_url": "https://ffmpegapi.net/download/uuid/audio_clip_5000_30000.mp3"
}
Note: Times are specified in milliseconds. For example, to extract audio from 5 seconds to 30 seconds, use start_time: 5000 and end_time: 30000.
Async Processing Example
curl -X POST "https://ffmpegapi.net/api/split_audio_time" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "audio_url": "https://example.com/long_audio.mp3",
    "start_time": 60000,
    "end_time": 120000,
    "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"
}

Trim Video

POST
/api/trim_video

Trim a video file by specifying start and end times in seconds. Perfect for extracting specific segments from longer videos.

Request Parameters
Parameter Type Required Description
video_url string Yes URL of the video file to trim (supports MP4, MOV, AVI, MKV, WebM, FLV, WMV)
start_time number Yes Start time in seconds (supports decimal values, e.g., 30.5). Must be >= 0
end_time number Yes End time in seconds (supports decimal values, e.g., 120.0). Must be greater than start_time
Example Request
curl -X POST "https://ffmpegapi.net/api/trim_video" \
     -H "X-API-Key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "video_url": "https://example.com/video.mp4",
       "start_time": 30,
       "end_time": 120
     }'
Success Response
{
  "success": true,
  "filename": "abc123_trimmed_video.mp4",
  "download_url": "https://ffmpegapi.net/api/storage/abc123_trimmed_video.mp4",
  "start_time": 30,
  "end_time": 120,
  "duration": 90,
  "message": "Video trimmed from 30.0s to 120.0s successfully"
}
Error Response
{
  "success": false,
  "error": "end_time must be greater than start_time"
}
Notes
  • Format Support: Supports MP4, MOV, AVI, MKV, WebM, FLV, WMV and other common video formats
  • Fast Processing: Uses stream copy (no re-encoding) for fastest possible trimming. Automatically falls back to re-encoding if stream copy fails
  • Precision: Supports sub-second precision (e.g., 30.5 seconds)
  • Time Validation: end_time must be greater than start_time, and start_time must be non-negative
  • Quality: Stream copy preserves original video and audio quality with no re-encoding
  • File Size: Video file URLs must be publicly accessible and under 100MB
Tip: Times are specified in seconds. For example, to extract from 1 minute 30 seconds to 3 minutes, use start_time=90 and end_time=180.
Code Examples
curl -X POST "https://ffmpegapi.net/api/trim_video" \
     -H "X-API-Key: YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "video_url": "https://example.com/long_video.mp4",
       "start_time": 60,
       "end_time": 180
     }'
import requests
import json

url = "https://ffmpegapi.net/api/trim_video"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}
data = {
    "video_url": "https://example.com/long_video.mp4",
    "start_time": 60,
    "end_time": 180
}

response = requests.post(url, headers=headers, json=data)
result = response.json()

if result["success"]:
    print(f"Video trimmed successfully!")
    print(f"Download URL: {result['download_url']}")
    print(f"Duration: {result['duration']} seconds")
    print(f"From {result['start_time']}s to {result['end_time']}s")
else:
    print(f"Error: {result['error']}")
const trimVideo = async () => {
  const response = await fetch('https://ffmpegapi.net/api/trim_video', {
    method: 'POST',
    headers: {
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      video_url: 'https://example.com/long_video.mp4',
      start_time: 60,
      end_time: 180
    })
  });
  
  const result = await response.json();
  
  if (result.success) {
    console.log('Video trimmed successfully!');
    console.log('Download URL:', result.download_url);
    console.log('Duration:', result.duration, 'seconds');
    console.log(`From ${result.start_time}s to ${result.end_time}s`);
  } else {
    console.error('Error:', result.error);
  }
};

trimVideo();
 'https://example.com/long_video.mp4',
    'start_time' => 60,
    'end_time' => 180
);

$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 "Video trimmed successfully!\n";
    echo "Download URL: " . $result['download_url'] . "\n";
    echo "Duration: " . $result['duration'] . " seconds\n";
    echo "From " . $result['start_time'] . "s to " . $result['end_time'] . "s\n";
} else {
    echo "Error: " . $result['error'] . "\n";
}
?>

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();

AI Captions (Remotion)

POST
/api/videos/add-tiktok-captions

Add animated TikTok-style karaoke captions to videos using AI. The endpoint downloads the video, extracts audio, transcribes with OpenAI Whisper (word-level timestamps), then renders word-by-word captions via Remotion. No subtitle file is required. Returns the captioned video plus caption artifacts (JSON, SRT, VTT).

Request Format

Content-Type: application/json

Parameters
Parameter Type Required Description
video_url String Yes URL to the source video file
subtitle_style String No Caption style preset. Options: plain-white, yellow-bg, pink-bg, blue-bg, red-bg. Default: plain-white
language String No Language code for transcription. Options: auto (detect), en, es, tr, fr, de, pt, ja, ko, zh, ar. Default: auto
aspect_ratio String No Output video aspect ratio. Options: 9:16, 16:9, 4:3, 3:4. Default: 9:16
position String No Vertical position of captions on screen. Options: top, center, bottom. Default: bottom
max_chars_per_line Number No Maximum characters per caption line (controls grouping). Default: 20. Range: 5–80
max_lines Number No Maximum caption lines displayed at once. Default: 1. Range: 1–4
Request Example
{
  "video_url": "https://example.com/video.mp4",
  "subtitle_style": "yellow-bg",
  "language": "auto",
  "aspect_ratio": "9:16",
  "position": "bottom",
  "max_chars_per_line": 20,
  "max_lines": 1
}
Response
{
  "success": true,
  "download_url": "https://ffmpegapi.net/.../captioned-video.mp4",
  "captions_json_url": "https://ffmpegapi.net/.../captions.json",
  "srt_url": "https://ffmpegapi.net/.../captions.srt",
  "vtt_url": "https://ffmpegapi.net/.../captions.vtt",
  "word_count": 33,
  "message": "Video with auto-generated TikTok captions rendered successfully"
}
Caption Style Presets
Style Description
plain-white White text with shadow (default)
yellow-bg White text on yellow background
pink-bg White text on pink background
blue-bg White text on blue background
red-bg White text on red background
Tip: Rendering is CPU-intensive and may take several minutes. Audio is limited to 25MB for Whisper; long videos are compressed to stay under that limit.
Code Examples
curl -X POST "https://ffmpegapi.net/api/videos/add-tiktok-captions" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://example.com/video.mp4",
    "subtitle_style": "yellow-bg",
    "language": "auto",
    "aspect_ratio": "9:16",
    "position": "bottom"
  }'
import requests

url = "https://ffmpegapi.net/api/videos/add-tiktok-captions"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}
data = {
    "video_url": "https://example.com/video.mp4",
    "subtitle_style": "yellow-bg",
    "language": "auto",
    "aspect_ratio": "9:16",
    "position": "bottom"
}

response = requests.post(url, headers=headers, json=data)
result = response.json()

if result["success"]:
    print("AI captions generated!")
    print("Download video:", result["download_url"])
    print("Captions JSON:", result.get("captions_json_url"))
    print("SRT:", result.get("srt_url"))
else:
    print("Error:", result["error"])
const response = await fetch('https://ffmpegapi.net/api/videos/add-tiktok-captions', {
  method: 'POST',
  headers: {
    'X-API-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    video_url: 'https://example.com/video.mp4',
    subtitle_style: 'yellow-bg',
    language: 'auto',
    aspect_ratio: '9:16',
    position: 'bottom'
  })
});

const result = await response.json();

if (result.success) {
  console.log('Download:', result.download_url);
  console.log('Captions JSON:', result.captions_json_url);
  console.log('SRT:', result.srt_url);
} else {
  console.error('Error:', result.error);
}

Text Overlay Captions (Remotion)

POST
/api/videos/add-text-overlay-captions

Add text overlay captions to a video. Each line of the provided text is displayed on screen for a configurable duration (default 5 seconds). No transcription or force alignment needed — ideal for motivational videos, quotes, and storytelling overlays. Uses the same Remotion rendering pipeline and caption styles as the AI Captions endpoint.

Request Format

Content-Type: application/json

Parameters
Parameter Type Required Description
video_url String Yes URL to the source video file
text String Yes Caption text with one line per caption. Each line is shown sequentially for the configured duration.
subtitle_style String No Caption style preset. Options: plain-white, yellow-bg, pink-bg, blue-bg, red-bg. Default: plain-white
aspect_ratio String No Output video aspect ratio. Options: 9:16, 16:9, 4:3, 3:4. Default: 9:16
position String No Vertical position of captions on screen. Options: top, center, bottom. Default: center
duration_per_line Number No Seconds each line is displayed. Default: 5. Range: 1–30
Request Example
{
  "video_url": "https://example.com/video.mp4",
  "text": "You are wonderful\nLife is beautiful\nNever give up",
  "subtitle_style": "yellow-bg",
  "aspect_ratio": "9:16",
  "position": "center",
  "duration_per_line": 5
}
Response
{
  "success": true,
  "download_url": "https://ffmpegapi.net/.../text-overlay-video.mp4",
  "line_count": 3,
  "duration_per_line": 5,
  "total_duration_seconds": 15,
  "message": "Video with text overlay captions rendered successfully"
}
Tip: Each line of the text parameter becomes a separate caption screen. Empty lines are ignored. Great for motivational quotes, storytelling, and informational overlays.
Code Examples
curl -X POST "https://ffmpegapi.net/api/videos/add-text-overlay-captions" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://example.com/video.mp4",
    "text": "You are wonderful\nLife is beautiful\nNever give up",
    "subtitle_style": "yellow-bg",
    "position": "center",
    "duration_per_line": 5
  }'
import requests

url = "https://ffmpegapi.net/api/videos/add-text-overlay-captions"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}
data = {
    "video_url": "https://example.com/video.mp4",
    "text": "You are wonderful\nLife is beautiful\nNever give up",
    "subtitle_style": "yellow-bg",
    "position": "center",
    "duration_per_line": 5
}

response = requests.post(url, headers=headers, json=data)
result = response.json()

if result["success"]:
    print("Text overlay video ready!")
    print("Download:", result["download_url"])
    print(f"Lines: {result['line_count']}, Duration: {result['total_duration_seconds']}s")
else:
    print("Error:", result["error"])
const response = await fetch('https://ffmpegapi.net/api/videos/add-text-overlay-captions', {
  method: 'POST',
  headers: {
    'X-API-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    video_url: 'https://example.com/video.mp4',
    text: 'You are wonderful\nLife is beautiful\nNever give up',
    subtitle_style: 'yellow-bg',
    position: 'center',
    duration_per_line: 5
  })
});

const result = await response.json();

if (result.success) {
  console.log('Download:', result.download_url);
  console.log(`Lines: ${result.line_count}, Duration: ${result.total_duration_seconds}s`);
} else {
  console.error('Error:', result.error);
}

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.