# FFmpeg Media Toolkit: Trim, Convert, Compress, GIF, Subtitles (`steady_kahikatea/media-toolkit`) Actor

Trim, convert, compress, extract audio or frames, make GIFs, and add or burn subtitles for video and audio files you supply (uploads or direct file URLs). Powered by FFmpeg. Video-platform links are not supported.

- **URL**: https://apify.com/steady_kahikatea/media-toolkit.md
- **Developed by:** [Nick Hellmich](https://apify.com/steady_kahikatea) (community)
- **Categories:** Videos, Developer tools, Automation
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## FFmpeg Media Toolkit: Trim, Convert, Compress, GIF, Subtitles

**Compress, convert and trim your own video and audio files in the cloud, with no software to install.** It's FFmpeg as an API: turn MP4 into MP3, MOV into MP4, a video into a GIF, shrink a video to a target size in MB, cut a clip, pull out thumbnails, or burn SRT subtitles into a video. Upload files or paste direct links (Google Drive and Dropbox share links are converted automatically). Pay only per output minute, and failed files are free.

> **You need the rights to your media.** Video-platform links (YouTube, TikTok, Instagram, Vimeo, X, Facebook…) are refused: this isn't a downloader.

### What people use it for

- **Video compressor:** make an MP4 smaller for email, Slack or upload limits, by quality level or a target size in MB, optionally downscaled to 720p or 480p.
- **Video converter:** MP4, WebM, MOV, MKV; audio to MP3, M4A, WAV, FLAC, Opus, OGG (for example **MOV to MP4**, **WebM to MP4**, **WAV to MP3**).
- **Extract audio from video:** **MP4 to MP3** for podcasts, transcription or music.
- **Video to GIF:** a short, palette-optimized GIF from any part of a clip.
- **Trim / cut video or audio:** keep a time range, fast (no re-encode) or frame-accurate.
- **Thumbnails and frames:** N evenly spaced frames, or one every N seconds, as JPG or PNG.
- **Add subtitles to a video:** burn in SRT/VTT/ASS captions, or add them as a soft track.
- **Batch jobs and automations:** up to 10 files per run; works from Zapier, Make, n8n, the API, MCP and AI agents.

### Quick start

1. Click **Start**. The prefill trims the first 5 seconds of a short public test clip (Jellyfish, 360p) for **$0.005**.
2. Choose an **Operation**, then upload files or paste direct links (up to 10 per run).
3. Open the **Output** tab and click **Download** on each row.

If you start a run with no files at all, it finishes immediately with a message telling you what to add, and nothing is charged.

### Ready-made inputs

| Task | Input |
|---|---|
| Compress a video to 720p | `{"operation": "compress", "maxHeight": 720, "quality": "medium", "fileUrls": ["https://example.com/video.mp4"]}` |
| Compress to about 8 MB | `{"operation": "compress", "targetSizeMB": 8, "fileUrls": ["https://example.com/video.mp4"]}` |
| MP4 to MP3 | `{"operation": "extract_audio", "format": "mp3", "fileUrls": ["https://example.com/talk.mp4"]}` |
| MOV to MP4 | `{"operation": "convert", "format": "mp4", "fileUrls": ["https://example.com/clip.mov"]}` |
| Video to GIF (5 s from 0:03) | `{"operation": "gif", "start": 3, "gifSeconds": 5, "fileUrls": ["https://example.com/clip.mp4"]}` |
| Trim 0:10 to 0:40 | `{"operation": "trim", "start": 10, "end": 40, "fileUrls": ["https://example.com/clip.mp4"]}` |
| 6 thumbnails | `{"operation": "extract_frames", "frameCount": 6, "fileUrls": ["https://example.com/clip.mp4"]}` |
| Burn in SRT subtitles | `{"operation": "subtitles", "subtitleUrl": "https://example.com/subs.srt", "fileUrls": ["https://example.com/clip.mp4"]}` |

### Operations and typical settings

| Operation | What it does | Key settings |
|---|---|---|
| Trim | Cut a time range. Fast (keyframe) by default, or frame-accurate | `start`, `end`, `accurate` |
| Convert | Change container or codec; downscale | `format`, `maxHeight` |
| Compress | Smaller file at a quality level, or a target size in MB | `quality`, `targetSizeMB`, `maxHeight` |
| Extract audio | Audio track as MP3, M4A, WAV… | `format`, `audioBitrate` |
| Extract frames | N evenly spaced frames, or one every N seconds | `frameCount` / `frameIntervalSeconds`, `imageFormat` |
| GIF | A short, palette-optimized GIF | `start`, `gifSeconds`, `gifWidth`, `fps` |
| Subtitles | Burn in, or add as a soft track (SRT, VTT, ASS) | `subtitleUrl`, `subtitleMode` |

### Output

There's one row per input with a `outputUrl` download link, along with `outputSeconds`, `outputHeight`, `billedEvents`, `charged`, `warnings` and `error`. Files are stored in the run's key-value store. Sample row:

```json
{ "source": "https://example.com/clip.mp4", "operation": "compress", "ok": true, "outputSeconds": 10.0,
  "outputHeight": 360, "billedEvents": { "video-minute-sd": 1 }, "charged": true,
  "outputUrl": "https://api.apify.com/v2/key-value-stores/.../records/ab12cd34-0-output.mp4" }
```

### Pricing (pay per event, per started output minute)

| Event | Price |
|---|---|
| Light minute: fast trim, audio extraction or conversion, soft subtitles | $0.005 |
| SD video minute: re-encoded output at 720p or below | $0.03 |
| HD video minute: re-encoded output above 720p, or WebM/VP9 | $0.08 |
| Frame extracted | $0.002 |
| GIF created | $0.02 |

Examples: extracting MP3 audio from a 10-minute video costs **$0.05**; compressing a 3-minute 1080p video to 720p costs **$0.09**; 10 frames cost **$0.02**. Billing is based on the **output** resolution, so downscaling to 720p uses the SD rate. **Failed files are never charged**, and that includes dead links, sign-in pages and files with no audio. Bronze, Silver and Gold plans get **10/20/30% off**. If your **Maximum cost per run** is too low for a file, the row tells you roughly how much it needs.

### Limits

Up to 10 files per run, 500 MB and 30 minutes per input, and outputs up to 1080p. Target-size compression bills two passes. Inputs are deleted after each file, and file contents are never logged.

### FAQ

**Google Drive or Dropbox link?** Paste the normal share link; the file must be shared with "Anyone with the link". Large Drive files may hit Google's virus-scan page; uploading the file directly always works.
**Why "NOT_A_MEDIA_FILE"?** The link returned a web page. Paste the direct file link (it usually ends in .mp4 or .mp3) or upload the file. You weren't charged.
**Why does fast trim start slightly early?** It cuts on keyframes so it can copy the stream without re-encoding. Set `accurate: true` for frame-exact cuts (billed as SD or HD minutes).
**Can an AI agent use it?** Yes, through the Apify API or MCP, and it supports x402 payments.

### Integrations: no-code and AI

#### Zapier: new video in Dropbox → compressed 720p copy → back to Dropbox

1. **Trigger:** Dropbox → *New File in Folder*.
2. **Action:** Apify → *Run Actor* → `FFmpeg Media Toolkit`, run **asynchronously** (encoding takes longer than Zapier's 30 s limit). Input: `{"operation": "compress", "fileUrls": ["{{Shared Link}}"], "maxHeight": 720, "quality": "medium"}`.
3. **Second Zap:** Apify → *Finished Actor run* trigger → *Fetch dataset items* → Dropbox *Upload File* from `outputUrl`.

#### Make: webinar recording → MP3 for the podcast feed

*Google Drive Watch Files* → *Apify Run an Actor* (`{"operation": "extract_audio", "format": "mp3"}`) → a separate scenario with *Apify Watch Actor Runs* → *Get Dataset Items* → *HTTP Get a file* (`outputUrl`) → upload to your podcast host.

#### n8n: thumbnails and a GIF preview for every new upload

*Webhook* → *Apify: Run an Actor and get dataset* (`{"operation": "extract_frames", "frameCount": 4}`, then a second call with `"operation": "gif"`) → *Airtable/Notion* row with the image links.

#### AI pipelines (LangChain / LlamaIndex)

Use it as a preprocessing step: pull audio out of a video before transcription, or extract frames for a vision model.

```python
from langchain_apify import ApifyActorsTool
media = ApifyActorsTool("steady_kahikatea/media-toolkit")
media.invoke({"run_input": {"operation": "extract_frames", "fileUrls": ["https://example.com/demo.mp4"], "frameCount": 6}})
```

**Sample input → output:** `{"operation": "extract_audio", "fileUrls": ["https://example.com/talk.mp4"], "format": "mp3"}` → `{"ok": true, "outputSeconds": 600, "billedEvents": {"light-minute": 10}, "outputUrl": "https://api.apify.com/v2/key-value-stores/.../talk.mp3", "charged": true}`. Cost: $0.05.

> **Tip:** Zapier's synchronous *Run Actor* times out after 30 seconds. For anything longer, run it asynchronously and start a second Zap from the **Finished Actor run** trigger. Make's synchronous timeout depends on your plan; use **Watch Actor Runs** for long jobs.

**Also works with:** the Apify API ("API" tab), the Apify MCP server for Claude/Cursor-style agents, and x402 payments.

**Docs used:** [Zapier](https://docs.apify.com/platform/integrations/zapier) · [Make](https://docs.apify.com/platform/integrations/make) · [n8n](https://docs.apify.com/platform/integrations/n8n) · [LangChain](https://docs.apify.com/platform/integrations/langchain) · [LlamaIndex](https://docs.apify.com/platform/integrations/llama-index)

### For AI agents

```
Actor: steady_kahikatea/media-toolkit
Input: {"operation": "compress", "fileUrls": ["https://example.com/video.mp4"], "maxHeight": 720, "quality": "medium"}
Output: dataset rows with outputUrl, billedEvents, charged, error
```

Questions or a file that fails? Use the Issues tab. If it's useful, a rating helps others find it.

# Actor input Schema

## `operation` (type: `string`):

What to do with each file.

## `fileUrls` (type: `array`):

Upload files or paste direct links to media files you have the rights to (e.g. https://example.com/video.mp4). Video/social platform links (YouTube, TikTok, Instagram, Vimeo, X, Facebook…) are rejected. Max 10 files, 500 MB and 30 minutes each.

## `format` (type: `string`):

For convert/compress/trim/extract_audio. Video: mp4, webm, mov, mkv. Audio: mp3, m4a, wav, opus, flac, ogg.

## `quality` (type: `string`):

Video quality vs file size. high = larger files, low = smallest.

## `maxHeight` (type: `integer`):

Downscale so the shorter side is at most this (never upscales). Output is capped at 1080. 720 or lower is billed at the cheaper SD rate.

## `targetSizeMB` (type: `number`):

Optional. Two-pass H.264 encode aimed at this file size (billed as 2× video minutes).

## `start` (type: `number`):

Trim/convert/GIF start time.

## `end` (type: `number`):

Trim/convert end time. Leave empty for the end of the file.

## `accurate` (type: `boolean`):

Off = fast keyframe cut without re-encoding (cheap). On = exact cut, billed as video minutes.

## `audioBitrate` (type: `string`):

For lossy audio outputs, e.g. 128k, 192k, 320k.

## `frameCount` (type: `integer`):

Number of evenly spaced frames to extract (max 100).

## `frameIntervalSeconds` (type: `number`):

Alternative to count: one frame every N seconds (max 100 frames).

## `imageFormat` (type: `string`):

jpg or png.

## `frameWidth` (type: `integer`):

Max frame width; never upscales.

## `gifSeconds` (type: `number`):

Max 30.

## `gifWidth` (type: `integer`):

Max 640.

## `fps` (type: `integer`):

Max 15.

## `subtitleUrl` (type: `string`):

Upload or link a subtitle file (max 1 MB). Required for the subtitles operation.

## `subtitleMode` (type: `string`):

burn = draw onto the video (re-encode, video minutes). soft = add a selectable subtitle track (no re-encode, cheap).

## Actor input object example

```json
{
  "operation": "trim",
  "fileUrls": [
    "https://test-videos.co.uk/vids/jellyfish/mp4/h264/360/Jellyfish_360_10s_1MB.mp4"
  ],
  "quality": "medium",
  "maxHeight": 1080,
  "end": 5,
  "accurate": false,
  "audioBitrate": "192k",
  "frameCount": 10,
  "imageFormat": "jpg",
  "frameWidth": 1280,
  "gifSeconds": 5,
  "gifWidth": 480,
  "fps": 10,
  "subtitleMode": "burn"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "operation": "trim",
    "fileUrls": [
        "https://test-videos.co.uk/vids/jellyfish/mp4/h264/360/Jellyfish_360_10s_1MB.mp4"
    ],
    "start": 0,
    "end": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("steady_kahikatea/media-toolkit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "operation": "trim",
    "fileUrls": ["https://test-videos.co.uk/vids/jellyfish/mp4/h264/360/Jellyfish_360_10s_1MB.mp4"],
    "start": 0,
    "end": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("steady_kahikatea/media-toolkit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "operation": "trim",
  "fileUrls": [
    "https://test-videos.co.uk/vids/jellyfish/mp4/h264/360/Jellyfish_360_10s_1MB.mp4"
  ],
  "start": 0,
  "end": 5
}' |
apify call steady_kahikatea/media-toolkit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steady_kahikatea/media-toolkit"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/lhHqWzw2dRsq0ahmV/builds/dKSvO6eWuteiWiiy3/openapi.json
