Video Stitching
Join ordered videos with matching aspect ratios and configurable picture and sound transitions.
Stitch 2–8 workspace videos into a new MP4. Clips play in array order; original assets remain unchanged. Different resolutions and frame rates are supported, but displayed aspect ratios must match. Cropping and padding are not supported.
Start a render
POST /api/v1/videos/stitch/render
Scope: videos:generate
Requires a prior credit purchase. A render costs 1 credit, matching caption and audio rendering. Failed renders refund the charged credit.
curl -X POST https://powerpost.ai/api/v1/videos/stitch/render \
-H "x-api-key: pp_live_sk_YOUR_KEY" \
-H "X-Workspace-Id: YOUR_WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"clips": [
{ "media_id": "11111111-1111-4111-8111-111111111111" },
{ "media_id": "22222222-2222-4222-8222-222222222222" },
{ "media_id": "33333333-3333-4333-8333-333333333333" }
],
"transitions": [
{ "type": "crossfade", "duration": 0.5, "audio": "crossfade" },
{ "type": "wipe_left", "duration": 0.3, "audio": "cut" }
]
}'| Field | Required | Meaning |
|---|---|---|
clips | Yes | 2–8 video references in playback order; repeated media IDs are allowed |
clips[].media_id | Yes | Video UUID in this workspace |
transitions | No | Exactly clips.length - 1 entries; omission means all straight cuts |
transitions[].type | Yes | Visual effect from the table below |
transitions[].duration | Yes | Overlap in seconds; 0 for cut, otherwise 1/30–5 |
transitions[].audio | No | cut or crossfade; defaults to cut for a visual cut and crossfade otherwise |
transitions[i] connects clips[i] to clips[i + 1]. Unknown fields are rejected, including order, remote URLs, custom filter expressions, and separate audio durations.
| Visual effect | Accepted values |
|---|---|
| Straight cut | cut |
| Blend | crossfade |
| Fade through a color | fade_black, fade_white |
| Wipe | wipe_left, wipe_right, wipe_up, wipe_down |
| Slide | slide_left, slide_right, slide_up, slide_down |
Timing and audio
Transitions overlap footage. Two 10-second clips with a 0.5-second transition produce approximately 19.5 seconds, not 20.5 seconds. Output runs at 30 fps: clip durations round down to complete frames, and transition durations round to the nearest frame. Each clip must retain at least one frame outside its incoming/outgoing overlaps. An invalid transition fails validation; it is never silently shortened.
Audio stays synchronized with each source video:
crossfadelinearly fades the first sound down and the second up throughout the visual overlap. Dialogue from both clips may be audible together.cutswitches sound at the overlap midpoint, trimming the unused audio from each clip. It does not shift the second clip's sound. With a visual cut, audio switches at the join.- A visual
cutrequires duration0and audiocut(or omitted). - Missing sound becomes silence. Short audio is padded; audio beyond the picture endpoint is trimmed. The first audio stream is used and its source timing is preserved.
For best results, stitch first, then mix music or narration, then add captions. Stitching does not retime separate caption cues.
Output and limits
- The first clip sets the output frame, capped at a 1920-pixel long side and 1080-pixel short side. Smaller first clips are not upscaled except for even-pixel rounding. Other clips are resized to that frame without cropping or padding.
- Output is H.264, 8-bit YUV 4:2:0, 30 fps, with 48 kHz stereo AAC in a fast-start MP4. Dimensions round to even pixels.
- Ratio comparison includes rotation and sample aspect ratio, allowing 0.2% relative rounding tolerance. For example, 720×1280 and 1080×1920 match; portrait and landscape do not.
- Maximum 120 seconds of total source footage, before overlaps. Repeated clips count each time.
- Maximum 500 MiB per input, 1 GiB combined input, and 500 MiB output. Downloads enforce actual byte limits even when stored metadata is missing or incorrect.
- Inputs must be self-contained MP4, MOV, or WebM, at most 4096 pixels on either side, 4096×2160 pixels in area, and 120 fps. Right-angle rotations are supported.
- HDR/wide-gamut inputs marked PQ, HLG, or BT.2020 are rejected; convert them to SDR first. Inputs without usable duration or video streams fail.
The API checks available duration and size metadata before charging. The worker verifies actual files, including displayed aspect ratio. A ratio mismatch is reported as a failed job with ASPECT_RATIO_MISMATCH, because stored dimensions alone cannot reliably represent rotation and non-square pixels. The render credit is refunded.
Start response
200 OK, consistent with the other render endpoints:
{
"stitch_render_id": "44444444-4444-4444-8444-444444444444",
"status": "processing",
"status_url": "/api/v1/videos/stitch/renders/44444444-4444-4444-8444-444444444444",
"created_at": "2026-09-16T12:00:00Z",
"remaining_credits": 42
}Get render status
GET /api/v1/videos/stitch/renders/{id}
Scope: videos:read
Poll every 2–5 seconds with the same authentication and workspace headers until completed or failed.
{
"stitch_render_id": "44444444-4444-4444-8444-444444444444",
"status": "completed",
"output_media_id": "55555555-5555-4555-8555-555555555555",
"error": null,
"created_at": "2026-09-16T12:00:00Z",
"updated_at": "2026-09-16T12:00:15Z",
"video": {
"media_id": "55555555-5555-4555-8555-555555555555",
"url": "https://temporary-signed-url.example/output.mp4",
"thumbnail_url": "https://temporary-signed-url.example/thumb.jpg",
"file_name": "video-stitch-55555555-5555-4555-8555-555555555555.mp4",
"file_size": 1234567,
"mime_type": "video/mp4",
"width": 1080,
"height": 1920,
"duration": 29.2,
"created_at": "2026-09-16T12:00:15Z"
}
}This duration assumes three 10-second clips and the example's 0.5/0.3-second overlaps. Store video.media_id; URLs are temporary. The output belongs to the first clip's folder. If the output is deleted, output_media_id and video become null.
While processing, output_media_id, video, and error are null. Failed jobs have status: "failed", no output video, and an error such as:
{
"code": "ASPECT_RATIO_MISMATCH",
"message": "Clip 2 has a different displayed aspect ratio from clip 1"
}Other worker error codes are INVALID_STITCH_INPUT and VIDEO_STITCH_RENDER_FAILED. Internal FFmpeg commands, paths, and diagnostics are not exposed in public errors. If a source is deleted before processing, the job fails and refunds its charge.
HTTP errors
| Status | Meaning |
|---|---|
400 | Invalid request, unsupported timing, unavailable clips, invalid storage path, or known size/duration limit exceeded |
401 | Invalid API key |
402 | Prior purchase required or insufficient credits |
403 | Missing scope or denied workspace/media access |
404 | Render not found in this workspace |
429 | Rate limit: 30 starts or 120 status requests per minute per key |
500 | Could not create/start the job |
MCP
| Tool | Purpose |
|---|---|
powerpost_render_video_stitch | Start with the same JSON body as REST |
powerpost_get_video_stitch_render | Poll using the render id |
FFmpeg implementation references
The renderer follows the official xfade, acrossfade, and concat contracts: matching video formats and timebases, zero-based concatenation segments, and audio overlap measured on the same timeline as video.