# Short Video Assembler – Merge Clips, Voiceover & Captions (`agentutilitylabs/short-video-assembler`) Actor

Turn 1-10 video clips plus an optional voiceover and SRT captions into one finished MP4 (vertical, horizontal or square, up to 60 s). Built for n8n, Make and AI-agent workflows. Pay per finished video, no subscription.

- **URL**: https://apify.com/agentutilitylabs/short-video-assembler.md
- **Developed by:** [Juan Carlos Morales](https://apify.com/agentutilitylabs) (community)
- **Stats:** 14 total users, 12 monthly users, 90.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$200.00 / 1,000 finished videos

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Short Video Assembler – Merge Clips, Voiceover & Captions to MP4

Turn 1–10 video clips, an optional voiceover and optional SRT captions into **one finished MP4**: vertical (Shorts, Reels, TikTok), horizontal (YouTube) or square, up to 60 seconds.

Built for automations that already generate clips and narration (n8n, Make, Zapier, AI agents) but can't run FFmpeg themselves, for example on **n8n Cloud, which has no Execute Command node**.

**Pay per finished video. No subscription, no monthly minimum.** Failed or invalid renders incur no video-assembly charge. The demo has no assembly charge. Platform compute is still incurred by the operator.

**Duration safety:** narration and timed captions must fit the output: total footage length, capped by `max_seconds` (maximum 60 seconds). Incompatible jobs are rejected before rendering and billing, with the measured durations and a corrective message. The tool does not silently cut narration or extend footage. For example, 25 seconds of clips plus 40 seconds of narration is rejected; supply more footage or shorter narration. Shorter narration is followed by silence. One output frame of tolerance accommodates media-container rounding.

**▶ See a real result:** [48-second vertical short made with this Actor](https://api.apify.com/v2/key-value-stores/emG7BAVjOvP3fYfJM/records/output.mp4?signature=1mYE9lM2e7CwzRfisNM7S): 4 public-domain NASA clips + a phone-recorded voiceover + 15 SRT captions → one MP4 in about 2 minutes. *(Footage: NASA. NASA does not endorse this product.)*

*You supply the clips, narration audio and caption timings; this Actor assembles them. It does not generate clips or voices, or auto-sync captions (the example's captions were timed by hand).*

### Use it for

**New to the workflow?** [Watch the narrated setup guide (1:47)](https://youtu.be/l1pk2BPfYC8), then [open the free n8n template](https://n8n.io/workflows/20412). The guide covers credentials, cached demo versus real input, billing and saving your MP4. Synthetic narration is used in the guide; this Actor does not generate voices or caption timings.

- AI video pipelines: generated clips + text-to-speech voiceover + captions → one publishable short
- Stitching scene clips (Hedra, Runway, Kling, Veo, stock footage) into a single video
- Adding burned-in captions to a sequence of clips

### Try the free demo

Run with `{"demo": true}` and get a pre-rendered 12-second sample built from test clips, a voiceover and captions. The sample is bundled with the Actor, so it is not rendered again for each demo request. This is a connection test, not a new video made from your content.

### Input

```json
{
  "clips": ["https://example.com/scene1.mp4", "https://example.com/scene2.mp4", "https://example.com/scene3.mp4"],
  "voiceover_url": "https://example.com/narration.mp3",
  "captions_srt": "1\n00:00:00,000 --> 00:00:03,000\nThree clips, one finished video\n",
  "orientation": "vertical",
  "fit": "crop",
  "max_seconds": 60
}
```

| Field | Meaning |
|---|---|
| `clips` | 1–10 public http(s) video URLs on standard ports 80/443, in playback order. DNS is pinned to validated public IPs for each connection and redirect. Any common format or size. Clip audio is not used. |
| `voiceover_url` | Optional public audio URL on port 80/443, with DNS pinned to validated public IPs per connection and redirect. Becomes the soundtrack; rejected if longer than the output, followed by silence if shorter. Without it, the video gets a silent audio track (platforms expect one). |
| `captions_srt` | Optional SRT text, burned in as white bold text with a black outline. |
| `orientation` | `vertical` 1080×1920, `horizontal` 1920×1080, `square` 1080×1080. |
| `fit` | `crop` fills the frame (default), `pad` keeps the whole picture with black bars. |
| `max_seconds` | Output length = total clip length, capped at this value (1–60). |

Limits: 200 MB per file, 600 MB per job.

Processing stops after a maximum of 15 minutes from application startup, even if a longer platform timeout is requested. An owner-controlled safety switch can pause new and active jobs during an incident. Safety-control outages also stop processing; no assembly fee is charged for unfinished renders.

URL safety: embedded credentials, nonstandard ports, private/non-global IP addresses, and redirects to those addresses are rejected. Each request connects only to an IP resolved and validated for that URL hop; environment proxies are not used.

### Output

One dataset row per job:

```json
{"status": "ok", "video_url": "https://api.apify.com/v2/key-value-stores/.../records/output.mp4",
 "duration_seconds": 42.0, "width": 1080, "height": 1920, "clips_used": 5,
 "voiceover_used": true, "captions_burned": true, "charged": true,
 "checks": {"duration_within_0_5s": true, "resolution_matches": true, "audio_stream_present": true, "non_empty_file": true}}
```

Every video is checked with FFprobe before you are charged: correct length, resolution, an audio track and a playable file. Output is H.264/AAC MP4 with fast-start for web playback.

### Pricing

One `video-assembled` event per finished, validated video. Demo runs, invalid input, failed downloads and failed renders incur no video-assembly charge. The operator still incurs platform usage costs, including downloads needed to determine media durations.

### Call it from n8n or any HTTP client

```
POST https://api.apify.com/v2/acts/agentutilitylabs~short-video-assembler/run-sync-get-dataset-items
Authorization: Bearer <your Apify API token>
Content-Type: application/json

{"clips": [...], "voiceover_url": "...", "captions_srt": "...", "orientation": "vertical"}
```

The response contains `video_url`.

**Ready-made n8n template:** [Merge video clips, voiceover, and captions into an MP4 with Apify](https://n8n.io/workflows/20412) — import it, add your Apify token, and run the free demo first.

### Privacy

Your files are downloaded only to render the job. The finished video is stored in the run's key-value store under your Apify account's retention settings. Only submit media you have the right to use.

# Actor input Schema

## `demo` (type: `boolean`):

Render a 12-second sample from built-in test clips. Not charged. Used only when no clip URLs are supplied.

## `clips` (type: `array`):

1-10 public http(s) URLs on standard ports 80/443, in playback order. DNS is pinned to validated public IPs for each request and redirect. Any common format and size; each clip is scaled to the output frame. Clip audio is not used.

## `voiceover_url` (type: `string`):

Optional public audio URL on standard port 80/443. DNS is pinned to validated public IPs per request and redirect. Becomes the soundtrack. Narration longer than the output is rejected before rendering, without a video-assembly charge. Shorter audio is followed by silence. No stretching or looping.

## `captions_srt` (type: `string`):

Optional numbered SRT cues with valid HH:MM:SS,mmm timings and text, burned into the video in white bold with a black outline. Every cue must fit the output duration; otherwise the job is rejected without a video-assembly charge. Max 50,000 characters.

## `orientation` (type: `string`):

Choose the output video aspect ratio and resolution.

## `fit` (type: `string`):

Choose whether clips fill the frame by cropping or preserve the full image with black bars.

## `max_seconds` (type: `integer`):

Output is the total clip length, capped at this value (1-60).

## Actor input object example

```json
{
  "demo": true,
  "clips": [],
  "orientation": "vertical",
  "fit": "crop",
  "max_seconds": 60
}
```

# Actor output Schema

## `video` (type: `string`):

The assembled, validated MP4 file.

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

Status, video URL, duration, resolution and validation checks.

# 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 = {
    "demo": true,
    "clips": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("agentutilitylabs/short-video-assembler").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 = {
    "demo": True,
    "clips": [],
}

# Run the Actor and wait for it to finish
run = client.actor("agentutilitylabs/short-video-assembler").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 '{
  "demo": true,
  "clips": []
}' |
apify call agentutilitylabs/short-video-assembler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agentutilitylabs/short-video-assembler"
        }
    }
}
```

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/o09SxHZkg59YVS3kx/builds/f8Lqt8bv8l3m2UZDi/openapi.json
