---
title: "Join clips"
method: POST
path: "/v1/studio/merge"
tags: ["Edit"]
---

# Join clips

`POST /v1/studio/merge`

## Request

- Base URL: `https://apihub.co.il`
- URL: `https://apihub.co.il/v1/studio/merge`
- Auth: HTTP bearer

Join 2–20 clips (each optionally trimmed) into one video, with cuts or transitions (fade, fade to black, slide, dissolve…). Different sizes are fitted to one frame. Several takes of one script: join them whole, then POST /v1/studio/edit the result with `remove_retakes: { window_s: <its length> }` to keep the best take of each sentence.

**Billing:** Per second of output (≤720p / 1080p tiers; audio and images cheaper) + transcription seconds when captions or silence removal transcribe automatically + the voice clean-up per second of output when asked for (`audio.denoise` / `enhance`, off by default) and only when it found noise to reduce or enhanced the voice (`auto` on a clean recording is free) + effect seconds for a heavy engine effect in `effects` (its seconds on screen × its measured weight — GET /v1/effects). Failed renders are not charged. · **Rate limit:** 30/min · **Asynchronous**

## Headers

- `Idempotency-Key` string
- `X-Tag` string
- `x-wait-ms` integer

## Request body

- object
  - `clips` object[], required
    - `end` number: seconds
    - `url` string, uri, required: Public URL of the source video (mp4, mov, webm, mkv…) — or the `url` of a previous render
    - `start` number: seconds
  - `output` object
    - `fit` 'crop' | 'blur' | 'pad': When the aspect ratio changes: crop the edges, blurred copy behind, or solid bars
    - `fps` number
    - `format` 'mp4' | 'webm' | 'mov' | 'gif' | 'mp3' | 'wav' | 'm4a' | 'jpg' | 'png'
    - `quality` 'draft' | 'standard' | 'high': high (default) = best picture; draft renders fastest for previews
    - `background` string: Bar colour for fit=pad
    - `resolution` '480' | '720' | '1080' | 'source': Short side in pixels (1080 = Full HD). `source` keeps the input size, max 1080
    - `aspect_ratio` 'original' | '9:16' | '16:9' | '1:1' | '4:5' | '4:3': 9:16 for Reels/TikTok/Shorts, 1:1 or 4:5 for feed, 16:9 for YouTube
  - `transition` object
    - `type` 'none' | 'fade' | 'fadeblack' | 'slideleft' | 'slideright' | 'circleopen' | 'dissolve'
    - `duration_ms` number

## Response `200`

Completed (or 202 while processing)

- object
  - `id` string: Execution id — stable across internal retries; use it with GET /v1/executions/{id}
  - `status` 'pending' | 'processing' | 'completed' | 'partial' | 'failed' | 'cancelled' | 'timeout'
  - `operation` string
  - `tag` string, nullable: Your X-Tag, if sent
  - `created_at` string, date-time
  - `completed_at` string, date-time, nullable
  - `output` object: Operation result (see each operation). While a Studio render is processing: `stage` (queued · transcribing · downloading · preparing · cleaning_voice · mixing_audio · rendering · finishing), `phase` (the engine's: download · prepare · voice · audio · encode · qa · upload, or null), `progress` (a percent, 0–100) and `progress_fraction` (0–1), `eta_seconds` (seconds left, never below 0) and `estimate_seconds`
  - `error` string, nullable
  - `usage` object
    - `credits_charged` number, nullable
    - `credits_reserved` number
    - `estimated_credits` number
    - `cost_source` 'provider_reported' | 'calculated' | 'estimated' | 'null', nullable
    - `provider_usage` object, nullable: Normalised consumption, e.g. { VIDEO_SECOND: 22.4 }
  - `balance` object
    - `available` number
    - `reserved` number

## Other responses

- `202`: Processing — poll GET /v1/executions/{id}
- `400`: Invalid input
- `402`: Insufficient credits / project budget
- `404`: Resource not found (or not yours)
- `429`: Rate limited
- `503`: Service temporarily paused

---

[API](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai.md) · [All operations](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/llms.txt) · [OpenAPI document](https://skmtc.dev/apihub/apis/apihub-one-api-for-chat-image-video-ai/revisions/a8caa42835ce?raw)
