Cut.ProDocs
Guides

Quickstart: your first clip

From the video link to the rendered MP4, with real requests

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"

Every example uses the base URL https://api.cut.pro/api/v1 and sends the key in the X-Api-Key header. If your key covers several workspaces, add X-Workspace-Id as well.

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" }'
Response
{
  "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 that id: it is the video_id of every call that follows.

Want to clip a file from your own machine instead of a URL? See Submitting a video for the upload flow.

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 }
  }'
Response 201
{
  "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.

strategy_id, template_id and source_language (auto, en, pt) are optional too. List your templates with GET /templates to get the IDs.

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"
Response
{
  "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

curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \
  -H "X-Api-Key: $CUTPRO_API_KEY"
Response
{
  "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

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"
Response 202
{
  "id": "7412908490112774",
  "edit_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

curl https://api.cut.pro/api/v1/renders/7412908490112774 \
  -H "X-Api-Key: $CUTPRO_API_KEY"
Response
{
  "id": "7412908490112774",
  "edit_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

curl https://api.cut.pro/api/v1/renders/7412908490112774/download \
  -H "X-Api-Key: $CUTPRO_API_KEY"
Response
{
  "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

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.id }),
});

const base = `/clips/${video.id}/submissions/${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.id}`);
	if (job.status !== "completed") {
		if (job.status === "queued" || job.status === "active") continue;
		throw new Error(job.status);
	}
	download = (await call(`/renders/${render.id}/download`)).url;
}

console.log(best.title, download);

What next?

On this page