Take the rendered clips to TikTok, Instagram, YouTube and more
Quickstart: your first clip
Seven requests stand between a YouTube link and a rendered vertical clip. This guide walks through all seven, in order, with the response of each one.
Before you start, create an API key and export it in your terminal:
export CUTPRO_API_KEY="your_key"1. Analyze the video
Section titled “1. Analyze the video”POST /clips/info reads the link metadata and works out the cost. Analyzing does not spend credits.
curl -X POST https://api.cut.pro/api/v1/clips/info \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }'{ "video_id": "7412908365112823", "title": "Full interview: 2 hours on building a career", "author": "Example Channel", "platform": "youtube", "duration": 7245, "credits_cost": 121, "current_balance": 480, "credits_unlimited": false, "force_watermark": false}Keep the video_id: it identifies the video in every call that follows.
2. Submit it for clipping
Section titled “2. Submit it for clipping”POST /clips creates the submission. Credits are charged here, at the amount the previous step showed.
curl -X POST https://api.cut.pro/api/v1/clips \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "7412908365112823", "timeframe": { "start": 0, "end": 1800 } }'{ "submission_id": "7412908371004912", "video_id": "7412908365112823", "status": "queued", "credits_charged": 30}The timeframe is optional: omit it to process the whole video, or narrow it to a stretch (in seconds) to spend less. Above, the first half hour cost 30 credits instead of 121.
3. Poll until it finishes
Section titled “3. Poll until it finishes”Poll the submission every 10 or 15 seconds. The status moves through queued, downloading, transcribing, video_analysis, analyzing and finalizing until it reaches completed (or failed).
curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912 \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "submission_id": "7412908371004912", "video_id": "7412908365112823", "status": "analyzing", "error_code": null, "clips_count": 0, "queue_position": 2, "estimated_time": 340}While the video waits in line, queue_position and estimated_time (in seconds) tell you how much is left. Once status turns completed, clips_count carries how many clips the AI produced.
4. Fetch the generated clips
Section titled “4. Fetch the generated clips”curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "clips": [ { "id": "7412908412887301", "title": "The mistake that cost him his first company", "rating": 9.2, "start_time": 412.5, "end_time": 461.8, "language": "en", "play_url": "https://media.cut.pro/preview/...", "download_url": "https://media.cut.pro/clip/...", "has_template_applied": false } ], "pagination": { "current_page": 1, "total_pages": 1, "total_count": 12, "has_next_page": false }}The rating is the AI score from 0 to 10: the higher it is, the more potential the clip has. Sort by it to take the best ones first.
5. Render a clip
Section titled “5. Render a clip”POST .../render produces the final MP4. To apply one of your looks first, call POST .../apply_template on the submission.
curl -X POST \ https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips/7412908412887301/render \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "render_id": "7412908490112774", "edit_setting_id": "7412908490112775", "status": "queued", "output_resolution": "1080p", "has_watermark": false, "from_cache": false, "download_url": null}When from_cache comes back true, the response is a 200 with download_url already filled in: that clip had been rendered with the same settings before, and you can skip the next two steps.
6. Poll the render
Section titled “6. Poll the render”curl https://api.cut.pro/api/v1/renders/7412908490112774 \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "render_id": "7412908490112774", "edit_setting_id": "7412908490112775", "status": "active", "progress": 64, "output_resolution": "1080p"}The status moves through queued, active and completed, and progress runs from 0 to 100.
7. Download the MP4
Section titled “7. Download the MP4”curl https://api.cut.pro/api/v1/renders/7412908490112774/download \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "url": "https://media.cut.pro/render/7412908490112774.mp4?signature=...", "filename": "the-mistake-that-cost-him-his-first-company.mp4"}The url is signed and valid for one hour. Download the file within that window or ask for another one.
The whole flow in one file
Section titled “The whole flow in one file”The seven steps chained together, polling included. Swap the video URL and run it.
const API = "https://api.cut.pro/api/v1";const headers = { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json",};
async function call(path, init) { const response = await fetch(`${API}${path}`, { ...init, headers }); const body = await response.json(); if (!response.ok) throw new Error(`${response.status} ${body.code}`); return body;}
const wait = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));
const video = await call("/clips/info", { method: "POST", body: JSON.stringify({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }),});console.log(`${video.title}: ${video.credits_cost} credits`);
const submission = await call("/clips", { method: "POST", body: JSON.stringify({ video_id: video.video_id }),});
const base = `/clips/${video.video_id}/submissions/${submission.submission_id}`;let state = submission;while (state.status !== "completed") { if (state.status === "failed") throw new Error(state.error_code); await wait(15); state = await call(base);}
const { clips } = await call(`${base}/clips`);const best = [...clips].sort((a, b) => b.rating - a.rating)[0];
const render = await call(`${base}/clips/${best.id}/render`, { method: "POST" });let download = render.from_cache ? render.download_url : null;while (!download) { await wait(10); const job = await call(`/renders/${render.render_id}`); if (job.status !== "completed") { if (job.status === "queued" || job.status === "active") continue; throw new Error(job.status); } download = (await call(`/renders/${render.render_id}/download`)).url;}
console.log(best.title, download);import osimport time
import requests
API = "https://api.cut.pro/api/v1"HEADERS = {"X-Api-Key": os.environ["CUTPRO_API_KEY"]}
def call(path, method="GET", **kwargs): response = requests.request(method, f"{API}{path}", headers=HEADERS, **kwargs) body = response.json() if not response.ok: raise RuntimeError(f"{response.status_code} {body.get('code')}") return body
video = call("/clips/info", "POST", json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"})print(f"{video['title']}: {video['credits_cost']} credits")
submission = call("/clips", "POST", json={"video_id": video["video_id"]})
base = f"/clips/{video['video_id']}/submissions/{submission['submission_id']}"state = submissionwhile state["status"] != "completed": if state["status"] == "failed": raise RuntimeError(state["error_code"]) time.sleep(15) state = call(base)
clips = call(f"{base}/clips")["clips"]best = max(clips, key=lambda clip: clip["rating"])
render = call(f"{base}/clips/{best['id']}/render", "POST")download = render["download_url"] if render["from_cache"] else Nonewhile not download: time.sleep(10) job = call(f"/renders/{render['render_id']}") if job["status"] == "completed": download = call(f"/renders/{render['render_id']}/download")["url"] elif job["status"] not in ("queued", "active"): raise RuntimeError(job["status"])
print(best["title"], download)What next?
Section titled “What next?”How credits are spent and how to check your balance