Skip to main content
POST
/
clippings
/
create
Create AI clipping job
curl --request POST \
  --url https://mvp.tryunsora.com/api/v1/clippings/create \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "sourceLang": "<string>",
  "targetLang": "<string>",
  "targetDuration": "auto",
  "query": "funny reactions",
  "limit": 10,
  "ratio": "original",
  "enableCaption": false,
  "captionStyle": "classic-yellow"
}
'
import requests

url = "https://mvp.tryunsora.com/api/v1/clippings/create"

payload = {
    "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "sourceLang": "<string>",
    "targetLang": "<string>",
    "targetDuration": "auto",
    "query": "funny reactions",
    "limit": 10,
    "ratio": "original",
    "enableCaption": False,
    "captionStyle": "classic-yellow"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: JSON.stringify({
    videoUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
    sourceLang: '<string>',
    targetLang: '<string>',
    targetDuration: 'auto',
    query: 'funny reactions',
    limit: 10,
    ratio: 'original',
    enableCaption: false,
    captionStyle: 'classic-yellow'
  })
};

fetch('https://mvp.tryunsora.com/api/v1/clippings/create', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://mvp.tryunsora.com/api/v1/clippings/create",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => json_encode([
    'videoUrl' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
    'sourceLang' => '<string>',
    'targetLang' => '<string>',
    'targetDuration' => 'auto',
    'query' => 'funny reactions',
    'limit' => 10,
    'ratio' => 'original',
    'enableCaption' => false,
    'captionStyle' => 'classic-yellow'
  ]),
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer <token>",
    "Content-Type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://mvp.tryunsora.com/api/v1/clippings/create"

	payload := strings.NewReader("{\n  \"videoUrl\": \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n  \"sourceLang\": \"<string>\",\n  \"targetLang\": \"<string>\",\n  \"targetDuration\": \"auto\",\n  \"query\": \"funny reactions\",\n  \"limit\": 10,\n  \"ratio\": \"original\",\n  \"enableCaption\": false,\n  \"captionStyle\": \"classic-yellow\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://mvp.tryunsora.com/api/v1/clippings/create")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"videoUrl\": \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n  \"sourceLang\": \"<string>\",\n  \"targetLang\": \"<string>\",\n  \"targetDuration\": \"auto\",\n  \"query\": \"funny reactions\",\n  \"limit\": 10,\n  \"ratio\": \"original\",\n  \"enableCaption\": false,\n  \"captionStyle\": \"classic-yellow\"\n}")
  .asString();
require 'uri'
require 'net/http'

url = URI("https://mvp.tryunsora.com/api/v1/clippings/create")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"videoUrl\": \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n  \"sourceLang\": \"<string>\",\n  \"targetLang\": \"<string>\",\n  \"targetDuration\": \"auto\",\n  \"query\": \"funny reactions\",\n  \"limit\": 10,\n  \"ratio\": \"original\",\n  \"enableCaption\": false,\n  \"captionStyle\": \"classic-yellow\"\n}"

response = http.request(request)
puts response.read_body
{
  "success": true,
  "data": {
    "id": "cmcl0x2ab0001abcd1234efgh",
    "status": "QUEUED",
    "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "config": {
      "sourceLang": null,
      "targetLang": null,
      "targetDuration": "DURATION_30_60",
      "limit": 5,
      "enableCaption": true,
      "captionStyle": "temp-7",
      "ratio": "RATIO_9_16"
    },
    "createdAt": "2026-06-10T12:00:00.000Z",
    "clips": [],
    "creditsUsed": 40
  },
  "creditsDeducted": 40,
  "creditsRemaining": 960
}
{
  "success": false,
  "error": "Invalid ratio. Use 9:16, 1:1, 4:5, 16:9, or original"
}
{
  "success": false,
  "error": "Insufficient credits. Need 40, have 10",
  "creditsRemaining": 10
}

Authorizations

Authorization
string
header
required

Clerk session JWT or Unsora API key (uns_live_*)

Body

application/json
videoUrl
string
required

Public URL of the source video (YouTube link or direct video URL).

Example:

"https://www.youtube.com/watch?v=dQw4w9WgXcQ"

sourceLang
string | null

Source language code of the video (e.g. en, es). Omit or null to auto-detect.

targetLang
string | null

Target language for clip titles, descriptions, and captions. Omit or null to keep the source language.

targetDuration
enum<string>
default:auto

Desired length range for each clip. Defaults to auto (up to 90s).

Available options:
auto,
lt30,
30-60,
60-90,
90-3min,
gt3min
query
string | null

Find Moments mode: natural-language description of the moments to extract (e.g. "funny reactions", "product demos", "goal moments and key plays"). When set, targetDuration is ignored. Omit to auto-detect the most viral clips.

Maximum string length: 500
Example:

"funny reactions"

limit
integer | null

Maximum number of clips to return, ranked by virality score (highest first). Omit to return all viral-worthy clips.

Required range: 1 <= x <= 20
ratio
enum<string>
default:original

Output aspect ratio. AI reframe keeps the main subject centered when cropping. Use 9:16 for TikTok/Reels/Shorts, 1:1 or 4:5 for feed posts, 16:9 for YouTube. Omit or use original to keep the source ratio.

Available options:
9:16,
1:1,
4:5,
16:9,
original
enableCaption
boolean
default:false

Burn animated captions into the exported clips.

captionStyle
enum<string>
default:classic-yellow

Caption style preset, named by look — the color is the accent on the spoken word. glow-* styles add a soft glow, static-* styles don't animate, gaming-* are bold streamer styles, white-card/black-box place text on a card. Only used when enableCaption is true. Defaults to classic-yellow.

Available options:
classic-yellow,
white-card,
black-box,
bold-white,
white-underline,
playful-green,
sketch-blue,
bubble-blue,
bubble-green,
glow-yellow,
duo-green,
duo-purple,
elegant-purple,
neon-cyan,
glow-pink,
bold-yellow,
soft-orange,
glow-green,
glow-orange,
retro-outline,
mint-bubble,
comic-duo,
static-outline,
static-comic,
static-minimal,
static-green,
static-yellow,
static-orange,
gaming-magenta,
gaming-green,
gaming-yellow,
gaming-white,
gaming-purple-box,
gaming-cyan,
gaming-orange-box

Response

Job queued. Clips render asynchronously — poll GET /clippings/status/{clippingId} until status is COMPLETED.

success
boolean
required
data
object
required
creditsDeducted
integer
creditsRemaining
integer